Introducing Sam Spade

Sam Spade

Sam Spade is a general-purpose Internet utility package, with some extra features to help in tracing the source of spam and other forms of Internet harassment.

  • Configuring Sam Spade
  • Using Sam Spade
  • Menu options
  • S-Lang scripting
  • Information about spam, the Internet, analysing headers...
  • Technical support
  • Elsewhere on the 'net
  • What you can do to help
  • Credits


Configuring Sam Spade

  1. Select the Edit menu item, then select Options...
  2. Select the Configure tab
    • Default Nameserver - set the internet nameserver Sam Spade uses for most name lookups
    • Maximum simultaneous connections - 100 is plenty
    • Your email address - this is used for email relay checking
    • Your ISPs webserver - used to keep a dialup connection alive
  3. Select the News tab
    You only need to configure this section if you plan to check for usenet cancels
    • News Server
    • Username, Password - if you need to enter a password to access your newsserver, enter it here
  4. Select the Advanced tab
    These three are potentially dangerous tools to enable. Be sure you understand the issues with them before you use them.
    • Enable zone transfers - enables DNS Zone Transfers
    • Enable active probing - enables port scanning
    • Enable relay checking - enables checking for an insecure mailserver


Using Sam Spade

Configuration

Before anything else you need to configure everything

Basics

Enter the address you're interested in the address box. If you've copied an address to the clipboard you can use the paste button or menu option to paste it into the address box.

Hitting return or clicking the Go button (the purple arrow) will identify the owners of the address using whois by default (it can be customised via the startup script

You can do all sorts of things to that address using the toolbar

The results will appear in a new window. Some parts of the results may be highlighted as active text. Right-clicking on the active text will popup a menu of things you can do next. Left clicking on it will do the most common operation.

If you're chasing down information about the owner of an address it's handy to be able to save the useful intermediate results. The two buttons on the left of the toolbar let you copy results to the Log Window for later use

Header analysis

If you have a suspicious looking set of email headers you can copy them to the clipboard from your mail reader and paste them into Sam Spade. They'll appear in a new window, with any addresses highlighted as active text. If you have Smart Paste enabled Sam Spade will run a rudimentary header analysis. It's not perfect, but can sometimes find suspicious parts of the header

Header analysis is only useful if you paste a full set of headers. See here for ways to turn on full headers on common mail programs

More advanced tools

There are some more sophisticated tools available on the Tools menu. Some of these may need to be enabled before you can use them.


S-Lang scripting

Scripting using S-Lang

Sam Spade has limited support for running scripts, using an embedded version of S-Lang, an interpreted language similar to C.

S-Lang command can be executed directly from the S-Lang command... option of the Tools menu or by hitting the space bar from the S-Lang console window

Basic S-Lang

S-Lang is a general purpose language, with a number of extensions for scripting interactive programs. It usually provides more flexibility than is used here. Better examples of S-Lang use are the jed text editor or the slrn scoring newsreader

A Guide to the S-Lang Language

S-Lang Run-Time Library Reference

Extensions

int MessageBox(string title, string message, int style)
Display a windows message box. style is passed directly to the Win32 MessageBox call
void ss_ping(string target, int count)
Open a ping window
void ss_whois(string query, string server)
Open a whois window
void ss_finger(string user, string host)
Open a finger window
void ss_dns(string query)
Open a dns window
void ss_dig(string query, string server)
Open an advanced dns window
void ss_vrfy(string user, string host)
Open an SMTP verify window
void ss_time(string host)
Open a time window
void ss_fasttrace(string host)
Open a fast traceroute window
void ss_slowtrace(string host)
Open a slow traceroute window
string ss_getinput()
Fetch the string in the address bar
string ss_getnameserver()
Fetch the current nameserver
void ss_addparagraph(string email_address, string tag, string message)
Add a paragraph at the top of the email message being composed. It will be prefixed by tag followed by two colons, or added to the paragraph already containing that tag if it exists. Also the email address will be appended to the To: field of the email message
string ss_abusenet(string domain)
Return the abuse desk email address or addresses for the given domain. If an address is filed at abuse.net it will be returned, otherwise a reasonable guess will be returned
void ss_write(string)
Write a string to the script console
void ss_error(string)
Write a string to the script console, highlighted as an error.

S-Lang functions defined in the startup file

If the following functions are defined in the startup file (usually startup.sl) then they may be called from within the program

ss_mac_default()
Called when the Go (arrow) button on the address bar is pressed, or enter is typed into the address bar
ss_mac_f2
ss_mac_f3
ss_mac_f4
ss_mac_f5
ss_mac_f6
ss_mac_f7
ss_mac_f8
ss_mac_f9
ss_mac_f11
ss_mac_f12
Called when the appropriate function key is pressed
ss_ab_clickthrough(string domain, string address)
ss_ab_dialup(string domain, string address)
ss_ab_dns(string domain, string address)
ss_ab_dropbox(string domain, string address)
ss_ab_misc(string domain, string address)
ss_ab_relay(string domain, string address)
ss_ab_this(string domain, string address)
ss_ab_webhost(string domain, string address)
Called when one of the abuse options is called from the context menu


Manuals and Tutorials

A selection of useful tutorials written by a number of different people

  • Spam Tracking 101 - analysing email headers and tracking down the sender
  • Spam Tracking 102 - searching usenet for information using DejaNews
  • Spam Tracking 103 - finding the owner of a domain or an IP address using whois
  • Spam Tracking 104 - a case study in tracking down a spammer
  • SpuTools - the canonical tutorial on analysing usenet headers
  • The alt.spam FAQ - Good info on deciphering forged email headers, and pointers to a lot of other online resources
  • IP Addresses and DNS - all about internet addressing
  • Decrypting pages encrypted using JavaScript
  • How traceroute works
  • Frequently Asked Questions with answers, about everything
  • Email Abuse
  • Email Abuse Resources
  • Internet Abuse
  • Net Abuse
  • Procmail
  • Responsible Net Commerce
  • Deciphering E-mail and Posts
  • Tactical Net Abuse
  • Teergrubing
  • CAUCE FAQ
  • Address Forgery
  • Spam-L
  • Filtering Mail

Reference

  • ISO3166 Country Codes
  • US Area Codes
  • International Dialling Codes
  • RFCs - the standards that make the internet work
    Internet Protocol
    791 1122
    Private Addresses
    1919
    Site Security
    2196
    Netiquette
    1855
    NNTP
    977
    HTTP
    2068
    Email
    SMTP
    821
    Email structure
    822 1138 1148 1327 2156
    POP3
    1939 1957
    Standard Mailboxes
    2142


Spam-tracking 101: by Bill Mattocks

Spam-tracking 101

Are you sitting comfortably? Good, then I'll begin.

Here is a spam I just received. It is bad, because it is spam. It is bad, because it attempts to masquerade as being information I requested to avoid detection as spam. It is bad because it has mangled headers to attempt to deflect complaints away from the true perpetrators. It is also quite funny. Here it is, dissected for the newer anti-spammers to watch and learn from:

First line:

>Received: from bullets.cybercon.com (bullets.cybercon.com [199.217.156.7])
>by mail.comp-sol.com (EMWAC SMTPRS 0.83) with SMTP id
><B0000036788@mail.comp-sol.com>; Wed, 10 Sep 1997 20:00:52 -0500

This is my mail server getting the spam from a mail server known as bullets.cybercon.com. Please note that the ISP listed here may well have been innocently hijacked by the spammer, we really don't know yet.

>Wed, 10 Sep 1997 21:02:53 -0500
>Received: from
>From: 84903020@ix.netcom.com

This is all fake, inserted by the spammer's bulk mail software. It can be safely ignored.

>Received: from 199.217.156.7 (hd70-155.hil.compuserve.com [199.174.250.155])
>	by bullets.cybercon.com (8.8.5/8.8.5) with SMTP id UAA03117;
>	Wed, 10 Sep 1997 20:27:30 -0500

This line purports to show where bullets.cybercon.com actually got the mail from that it relayed to me. Please note that "199.217.156.7" does not belong to "hd70-155.hil.compuserve.com." How do we know this? Simple, we use a tool called nslookup (available for many platforms). Here is what we see:

[199.217.156.7]
Translated Name: bullets.cybercon.com
IP Address: 199.217.156.7

[hd70-155.hil.compuserve.com]
Translated Name: hd70-155.hil.compuserve.com
IP Address: 199.174.250.155

What does this all mean? It means that the first part of the line is bogus, but the second part is correct. We know that because most mail server software will report accurate information about where it got the mail from in most cases (it has to be misconfigured or older brain-dead software to be completely silent about where it got the mail from). It has been my observation that you can trust the IP address found within the square brackets, i.e. "[199.174.250.155]"

So, we have a reasonable expectation that the spammer used a dialup account on Compuserve to send this spam. We still do not know if the ISP it was sent through is innocent or guilty, though. We will complain to Compuserve at abuse@compuserve.com, for starters.

>Received: from usr15-dialup53.mx1.Willowsprings.mci.net [166.55.38.181]
    by Willowsprings.mci.net (8.8.5/8.6.5) with SMTP id GAA02664
    for <bullwinkle@rocky.com>; Wed, 10 Sep 1997 20:59:04 -0600 (EST)
>Date: Wed, 10 Sep 97 20:59:04 EST
>To: bullwinkle@rocky.com
>Subject: Here's  the info you requested
>Message-ID: <19970908182053.load2391.in@don>
>Reply-To: mrchicken@answerme.com
>X-UIDL: 12345678987456123012345698745612
>Comments: Authenticated sender is <don@Willowsprings.mci.net>
The above is all trash. You can ignore any headers after the correct ones are found. That is because mailers put the headers onto the top of the message when they pass it along, not somewhere inside the message. Thus, the very top message was from my mailer, receiving the mail. The one right under that was from the ISP's mailer, sending it to me and reporting where it got it from. The rest is junk, designed to confuse us.

Don't be fooled by "Authenticated sender" messages. They are easily faked, and mean nothing. They don't "authenticate" anything.

><HTML><PRE><BODY BGCOLOR="#000000"><FONT COLOR="#00FFFF" SIZE=3>
>Everybody loves Mr. Chicken!

Ah, here's where it gets amusing! So, let's just enjoy this spam for a moment, shall we?

>Kids are going wild over Mr. Chicken.  Parents laugh hysterically at the sight of him.
>Why spend $50 on toys that your kids forget about the next day when for pennies
>they can have a Mr Chicken that they'll enjoy for months?
>For full details, Email MrChicken@answerme.com

Now, if we follow Rush Limbaugh's advice and "follow the money," it would appear that the perpetrator of this spam has a mailbox at answerme.com, and his handle is "MrChicken." What do we know about answerme.com?

Cyber Promotions (ANSWERME4-DOM)
8001 Castor Avenue, Suite #127
Philadelphia, PA 19152
USA

Well, it happens that Cyberpromo is the owner of this particular domain. That kind of ends that trail for us, because Cyberpromo is a spamhaus, and their upstream provider, AGIS, is well aware of it and supports it. AGIS is a "backbone" on the Internet, so there is no one above them to complain to. Still, since Cyberpromo CLAIMS to be against illegal relaying, we can send a copy of the complaint to relayabuse@cyberpromo.com and also to abuse@agis.net. This won't do anything, but what the heck.

></FONT><FONT  COLOR="#000000" SIZE=3>

So, that ends the spam. Now, what about the original ISP who sent the spam to me? Innocent party or spamhaus? Well, let's take a look at their web page: http://www.cybercon.com/aup.html

Cybercon Acceptable User Policy

It is contrary to Cybercon policy for any user to effect or participate in any of the following activities through a Cybercon service:

[snip]

3. To send unsolicited mass emailings to more than twenty-five (25) email users, if such unsolicited emailings provoke complaints from the recipients;

4. To engage in any of the foregoing activities using the service of another provider, but channeling such activities through a Cybercon account or remailer, or using a Cybercon account as a maildrop for responses;

Now, it would appear from looking at their homepage (http://www.cybercon.com/) and also by "reading between the lines" of their AUP, that Cybercon is a spamhaus, however thinly disguised. That does not mean that they authorized this spam, or that they were not hijacked. But the suspicion is definitely there. In any case, they get a copy of the complaint as well. If they were hijacked, they may wish to investigate further and perhaps initiate legal action. If they were not, they may remain silent on the matter. In any case, they also have an upstream provider, which can be determined by doing a traceroute on "bullets.cybercon.com"

 1  156.46.104.254 (156.46.104.254)
 2  alpha-nomad.alpha.net (206.190.31.149)
 3  mke-1.alpha.net (156.46.1.1)
 4  chicago2-cr2.bbnplanet.net (204.167.132.9)
 5  chicago1-br1.bbnplanet.net (199.92.131.11)
 6  core5-hssi5-0.WillowSprings.mci.net (206.157.77.201)
 7  core1.NorthRoyalton.mci.net (204.70.4.205)
 8  core-hssi-2.Chicago.mci.net (204.70.1.93)
 9  border4-fddi-0.Chicago.mci.net (204.70.3.83)
10  startnet-llc.Chicago.mci.net (204.70.27.6)
11  router.cybercon.com (199.217.252.58)
12  bullets.cybercon.com (199.217.156.7)

So, we know they get their service from mci.net. Therefore, a complaint also goes to abuse@mci.net.

What else do we know about the elusive Cybercon? Let's check their IP range, to see who might own it. We can use "whois"

whois 199.217.156.0
[rs.internic.net]
STARNET, L.L.C. (NETBLK-STARNET-CBLK)
P.O. Box 6286
St. Louis, MO 63006-6286

Netname: STARNET-CBLK
Netblock: 199.217.128.0 - 199.217.255.0
Maintainer: STLL

Coordinator:
Myers, Chris B. [President]  (CBM10)  chris@STARNET.NET
(314) 227-3136 (FAX) (314) 716-6163

Domain System inverse mapping provided by:

ADMIN.STARNET.NET            199.217.253.10
NEWS.STARNET.NET             199.217.253.11
NS1.DRA.NET                  192.65.218.14

Record last updated on 30-Aug-96.

So, it appears that Starnet owns their Class "C" license. Now, let's jump into DejaNews (the land of "all my sins remembered") and see what we can find out:

For "cybercon.com," we find only this:

*******************QUOTE*******************
2 Matches for search:  cybercon.com

1. 97/05/18 016 [email] Information /uu. news.admin.net-abus LINDSEY
JEAN NICE <
2. 97/03/01 016 [email]-BETTER THAN AOL  news.admin.net-abus LINDSEY
JEAN NICE <
******************ENDQUOTE*****************

Upon reading the messages in question, it appears that they once complained that they had been mischaracterized as "cybercoM.com" and not "cybercon.com" and wanted a retraction printed. OK, no spam reports. How about their class C ticket holder?

[nothing of consequence found]

What about doing a search for "mrchicken?"

Here is what we find:

**********************QUOTE**********************

Subject:      Everyone loves Mr Chicken
From:         igynews@sprynet.com
Date:         1997/09/08
Message-Id:   <5uv7e4$qiv$1@juliana.sprynet.com>
Organization: Sprynet News Service
Newsgroups:   alt.activism.children
[Fewer Headers]

EVERYONE LOVES MR. CHICKEN!

Are you tired of paying hundreds of dollars for toys your kids break
or get bored of the next day?  How would you like a toy that can
provide countless hours of fun for literally pennies?  MR. CHICKEN is
the answer.  For full details, email MRCHICKEN@answerme.com

**********************ENDQUOTE**********************

So, it appears that MrChicken has posted an identical message a few days ago in UseNet. Just one, so not spam, although since it just happened, the others may not have been picked up by DejaNews yet. Still, we see that sprynet.net was used, not cybercon.com. It begins to look as though cybercon.com is not guilty, but either was hijacked or has a bad actor on their hands. So, we still complain to Cybercon, but scratch abuse@mci.net (their upstream provider) from the list.

Now, it appears that we have done "due diligence" on our search to find the source of the spam. We believe that the guilty party is only mrchicken@answerme.com. So, here is our complaint e-mail:

To: mrchicken@answerme.com

[Note - this will get me a response from their autoresponder, which may give me more information on the identity of "Mr. Chicken." However, it may also subject me to more spam. I am willing to risk it, for the sake of the exercise. You probably do not want to do this.]

From: bmattocks@comp-sol.com

Subject: SPAM REPORT →Re: Here's  the info you requested

CC: staff@cybercon.com,support@cybercon.com, abuse@agis.net,relayabuse@cyberpromo.com

NOTE TO CYBERCON.COM:  It would appear that your SMTP server was
either hijacked, or you have a "bad actor" on your hands.  Could you
please investigate and take action on this?

NOTE TO CYBERPROMO:  It would appear that a client of yours
(MrChicken@answerme.com) is failing to use your relay service, and may
have hijacked the SMTP server belonging to cybercon.com.  Please
investigate and take action!

NOTE TO AGIS.NET:  This spam was sent via what may well have been an
illegally hijacked SMTP server.  Please investigate and take action.

Thanks,

Bill Mattocks
Computer Solutions of Kenosha
http://www.comp-sol.com

>Received: from bullets.cybercon.com (bullets.cybercon.com [199.217.156.7]) >by mail.comp-sol.com (EMWAC SMTPRS 0.83) with SMTP id ><B0000036788@mail.comp-sol.com>; Wed, 10 Sep 1997 20:00:52 -0500
>From: 84903020@ix.netcom.com
>Received: from 199.217.156.7 (hd70-155.hil.compuserve.com [199.174.250.155])
>	by bullets.cybercon.com (8.8.5/8.8.5) with SMTP id UAA03117;
>	Wed, 10 Sep 1997 20:27:30 -0500
>Received: from usr15-dialup53.mx1.Willowsprings.mci.net [166.55.38.181] by Willowsprings.mci.net (8.8.5/8.6.5) with SMTP id GAA02664 for <bullwinkle@rocky.com>; Wed, 10 Sep 1997 20:59:04 -0600 (EST)
>Date: Wed, 10 Sep 97 20:59:04 EST
>To: bullwinkle@rocky.com
>Subject: Here's  the info you requested
>Message-ID: <19970908182053.load2391.in@don>
>Reply-To: mrchicken@answerme.com
>X-UIDL: 12345678987456123012345698745612
>Comments: Authenticated sender is <don@Willowsprings.mci.net>

><HTML><PRE><BODY BGCOLOR="#000000"><FONT COLOR="#00FFFF" SIZE=3>
>Everybody loves Mr. Chicken!

>Kids are going wild over Mr. Chicken.  Parents laugh hysterically at the sight of him.
>Why spend $50 on toys that your kids forget about the next day when for pennies
>they can have a Mr Chicken that they'll enjoy for months?
>For full details, Email MrChicken@answerme.com

></FONT><FONT  COLOR="#000000" SIZE=3>

OK, folks, that's it for tonight. Any questions? If not, class is dismissed.


Spam-tracking 102: the many uses of DejaNews; by Bill Mattocks

Spam-tracking 102:
the many uses of DejaNews

by Bill Mattocks

Are you sitting comfortably? Good, then I'll begin.

Since posting Lesson 101 (spam tracking for newbies), I have received a few comments by people curious about DejaNews and how it works. Quite simply, DejaNews is one of the dedicated spam-tracker's most powerful tools, and it is quite simple to use.

DejaNews is a free service and trademark of Deja News ™ is a trademark of Deja News Research Service, Inc. They make their money by selling banner advertising that the user sees across the top of their screen when it is used.

DejaNews is the memory of UseNet. There are ways to prevent a given UseNet message from being archived by DejaNews, and there are ways to remove your own information from DejaNews, but for legitimate spam-trackers, that's not important. For the most part, DejaNews simply records a major part of UseNet News traffic, and indexes every single word of it (that's important, as we'll see later).

What is important is that DejaNews has many powerful features that we can use to track spam back to its source.

Keeping in mind that most spammers have been at it for awhile, we can use DejaNews for the following:

  1. Determining from where and for how long a spammer has been spamming.
  2. Determining if anyone has succeeded in unmasking the spammer yet.
  3. Determining if the spammer has given away clues to his or her own identity over a period of time.

To expand on that:

1) By searching for a spammer's name or address, we can see if the spammer has been spamming from more than one location over time. In effect, we can track the spammer's history as they are kicked from ISP to ISP. This is useful information!

Often, we get "I'm sorry" responses from clueless ISPs or even the spammers themselves, who want to fool us into thinking that they are "beginners" at the spamming game. DejaNews can put the lie to this one right away! If an ISP gets a spam report, that's one thing. But, if the ISP gets a report that gives detailed information on just how long the spammer has been at it, and how they've been kicked from ISP to ISP, that's quite another. It may be enough to convince some ISPs to dump the spammer, since he has been lied to. In any case, you'll know when NOT to believe the clever "I'm sorry" lie.

2) One can see how quickly the messages fly by in NANAE. This can make it hard to recall exactly who said what to whom and when. Often, a spammer is unmasked due to the hard work of some anti-spammer, and then is RE-unmasked by another anti-spammer 6 weeks later. Before investing all of your time and energy in tracking a spammer, do yourself a favor and see if the job has already been done for you! Of course, you must still exercise due diligence to be certain that the spammer is the same one you're after, but take the time to look! The power of DejeNews lets you network with anti-spammers who are speaking to you, as it were, from the past.

3) Spammers often change little bits and pieces of their spam as they fine-tune it. If they find that they've left themselves vulnerable, they change the ugly bit and continue on. They hope that nobody realizes that they've fixed whatever it was that gave them away in the first place. However, DejaNews is the answer here. By comparing past and present spams from the same spammer, one can find interesting things which can finish a puzzle sometimes. This is not frequent, but it does happen. Keep your eyes open for subtle changes in a spammer's methods that might indicate a weak link.

HOW TO USE DEJANEWS:

Quite simple to begin. Go to http://www.dejanews.com/ and type in the name or mailing address of the spammer. Click on the FIND button. However, sometimes it is not as simple as all of that. Fortunately, as I said earlier, DejaNews indexes the COMPLETE TEXT of all that it collects. Given that, you can search on random bits of text that can shed light on the identity of a spammer. Is he using a PO Box? If so, type that in. You'd be amazed at how many spammers are too cheap to get a new PO Box after they're unmasked at one spam and move on to another. Same PO Box generally means same spammer. Phone numbers. Searching for ISPs can give a clue as to whether or not they've been known to host spammers. Use your imagination! Try matching up the IP address that the spammer came in from. That is less useful, since most IP numbers are pseudo-random when they're hosting a dialup account, but you never know. It might be an IP address that's been made to look like a dialup, but is really a dedicated circuit. You have to think a bit like a detective. Use logic and reasoning to satisfy yourself that a hit is or is not the spammer you're looking for. Even a ".sig" line can ID a spammer sometimes. Spammers are often quite gray little blobby creatures, devoid of individual traits, but sometimes one burns with a bit of creativity, or happens to seize upon a certain phrase which they like to use over and over. It can be their undoing.

Don't forget to search all the way back in DejaNews. At the end of the initial search, you'll see another block with your original search in it, and a couple of radio buttons for "recent" and "old" news. The default that you've just completed is recent. Make sure to check out the "old" news as well.

DejaNews has many powerful features, including a power search mode. I encourage you to explore those features as well, although you'll have to learn a bit about boolean logic, which is beyond the scope of this lesson.

That's it for now. Remember, DejaNews is a big hammer for the anti-spammer. Don't be afraid to use it to clobber a spammer.

Best Regards,

Bill Mattocks, CIIU

Derived from an HTML translation by Marek Jedlinski of a usenet post by Bill Mattocks


Spam-tracking 103: WHOIS database; by Bill Mattocks

Spam-tracking 103:
the WHOIS database

by Bill Mattocks

Many thanks to all; I have received a warm response to Spam-tracking 101 & 102. I was intending to do 103 in a few days, but what the heck, you all have warmed the cockles of my heart! (What exactly is a cockle, anyway?) So here it is - Spam-tracking 103, the WHOIS tool.

By the way, there are a lot of folks out there who know MUCH more about spam-tracking than I do. Please feel free to put out your own curriculum. The important thing is that we educate this flood of newbies and lurkers who hate spam as much as we veterans do. We want everyone to get into the the spam-fighting biz, don't we?

Are you sitting comfortably? Good, then I'll begin.

Since the Internet was essentially started by stringing together a bunch of computers that spoke the Operating System known as Unix, most of the administrative and client tools that act over the internet were originally Unix tools, as well. Even UseNet was at one time only readable by Unix machines and those who had mastered its arcane command set. There are still a bunch of us old grizzled veterans around, and you will see us speaking to each other in bizarre terms and buzzwords, and you might wonder what the heck we're talking about.

Fortunately, most, if not all, of the tools that are used on the Internet have been reproduced in a more user-friendly, graphical, way in the Windows, Windows 95, and MacIntosh arenas. This, then, is an introduction to the major tools that systems administrators (sysadmins) use to track down Internet problems. Also fortunately, most of them are readily adaptable to search out the roots of spam.

Shameless Plug (I have nothing to do with this company, but I like their software):

My personal favorite is NetScan Tools for Windows 95. It has a very nice user interface, and it contains all kinds of tools other than just whois - we will cover those in future lessons, since WHOIS is a big topic. You can get a shareware copy of it at: http://www.nwpsw.com/. It is a free 30-day evaluation copy of the tool. It is expected that you will register and pay for it if you use beyond 30 days. It costs $US25, and I believe it is money well spent. End of plug.

You can obtain these tools from a variety of sources. I recommend taking a look at http://www.tucows.com/ but there are many other sources, such as http://www.download.com/ and http://cws.internet.com/.

Besides Unix, I am most familiar with Windows/Win95, so these are the tools I will refer to. If anyone knows of their Mac analogue, perhaps they would post those tools as a followup to this message.

In this lesson, we begin with WHOIS:

The WHOIS database is a compendium of domain names. You may already know that the Internet itself does not speak "human" or "english." It speaks in IP (Internet Protocol) addresses. Those addresses have to be linked to the human domain names in a database in order to be useful. If I were typing from a Unix prompt, I would type the commands like this:

whois comp-sol.com

-or-

whois 156.46.104.0

-or-

whois '!NETBLK-SPRINT-CF284F'

The point here is that WHOIS can be used in a variety of ways to query the infomation contained therein. Sometimes, you may get a bewildering response from Internic, but there is usually something further that you can query to track a source of spam. If you don't know how to begin, try just typing in whois "anything" and see what you get. You won't break it or make anyone mad at you.

QUERYING A DOMAIN NAME

If you query a domain name, say "spamlovers.com" and get a "No Response Found" reply from Internic, that means that it is NOT a legitimate domain name, because Internic has authority over all domain names that end in .com. Same for .net, .org, and .edu. Notice, please that you must enter "spamlovers.com" and not "www.spamlovers.com" or "spammachine.spamlovers.com" to get a positive response. It is just the last bit of the domain name in front of the dot that we are interested it. The bit in front of "spamlovers.com" denotes a machine belonging to that organization, but it is named locally, not by Internic.

Now, on to the domain name. If you are querying a US domain name, and it is legitimate, you should get back a response, and it may look something like this:

whois comp-sol.com

[rs.internic.net]
Computer Solutions of Kenosha (COMP-SOL-DOM)
   2031 22nd Avenue
   Kenosha, WI 53140
   US

   Domain Name: COMP-SOL.COM

   Administrative Contact, Technical Contact, Zone Contact:
      Mattocks, Bill  (BM561)  bmattocks@COMP-SOL.COM
      (414)551-8088
   Billing Contact:
      Mattocks, Bill  (BM561)  bmattocks@COMP-SOL.COM
      (414)551-8088

   Record last updated on 06-Sep-97.
   Record created on 09-Aug-95.
   Database last updated on 12-Sep-97 04:47:08 EDT.

   Domain servers in listed order:

   DARKSTAR.NOMAD.NET           156.46.104.2
   NOMAD.NET                    156.46.104.1

The InterNIC Registration Services Host contains ONLY Internet
Information (Networks, ASN's, Domains, and POC's).
Please use the whois server at nic.ddn.mil for MILNET Information.

Let's deconstruct the information and see what it means:

Computer Solutions of Kenosha (COMP-SOL-DOM)
   2031 22nd Avenue
   Kenosha, WI 53140
   US

OK, so this is a supposedly a business, called Computer Solutions of Kenosha, in Kenosha, Wisconsin. The "(COMP-SOL-DOM)" bit indicates that comp-sol.com is indeed the domain name. Please bear in mind that spammers are becoming educated about domain names and whois. They often put bogus information in when they register with Internic to get their domain name. That's against the rules, but Internic won't do anything about it at this time. We live with what is. Still, many times this information will be correct. If nothing else, Internic has to have a way to bill the domain. If the information given is totally bogus, the spammer probably intends not to pay the bill, but merely to use the domain name until it expires, and then register a new one. Let's move on:

   Administrative Contact, Technical Contact, Zone Contact:
      Mattocks, Bill  (BM561)  bmattocks@COMP-SOL.COM
      (414)551-8088
   Billing Contact:
      Mattocks, Bill  (BM561)  bmattocks@COMP-SOL.COM
      (414)551-8088

This bit tells us who is responsible for the domain, who pays the bills, who keeps it running, etc. Again, it is supposed to contain legitimate information, and again, it often does not. Just the same, if the information is accurate, we now have an e-mail address to complain to. Hmm, happens to be me, doesn't it. Well. Please don't take me too literally. We also have a telephone number to call if we wish to register a complaint that way.

   Record last updated on 06-Sep-97.
   Record created on 09-Aug-95.
   Database last updated on 12-Sep-97 04:47:08 EDT.

This bit is not too exciting. It is as it appears, showing us when the domain was created, and when it was last changed.

NOTE: Remember our last lesson about DejaNews? If the "Record last updated" date is fairly recent, this would be a good time to search for the offending domain name using Dejanews (http://www.dejanews.com) to see if any other anti-spammer has posted similar WHOIS information. As a spammer hops from ISP to ISP, they take their domain names with them, and that will show up. Just a tiny bit of information, but it may help to prove to your satisfaction that a spammer is indeed a spammer, and that a particular domain is or is not a spamhaus. It's the little things...

   Domain servers in listed order:

   DARKSTAR.NOMAD.NET           156.46.104.2
   NOMAD.NET                    156.46.104.1

Finally, we have the bit about the Domain servers. A domain server is simply the machine that does lookups for a particular domain name when someone sends anything to that domain, like when you go to a web page or when you send mail to a particular domain. In this case, if anyone goes to a web page at www.comp-sol.com, their request will be "looked up" by one or both of the machines above. This is important, because a spammer may receive his upstream account (or feed) from one source, and have another source do his DNS or Domain Name Service. It could be another source to complain to. Many times, when you are dealing with a spammer, you will see this:

   Domain servers in listed order:

   NS7.CYBERPROMO.COM		205.199.2.250
   NS5.CYBERPROMO.COM		205.199.212.50
   NS8.CYBERPROMO.COM		207.124.161.65
   NS9.CYBERPROMO.COM		207.124.161.50

And you know you are dealing with the deathstar itself. To many of us here on NANAE, seeing this is final and irrevocable proof that the domain in question is a spamhaus, and the sender of the e-mail is a spammer. We tend not to believe that there are any legitimate domains hosted by Cyberpromo.

When you see this, it is like swimming in the ocean and seeing a dorsal fin rise up out of the water and start towards you.

USING WHOIS TO LOOK UP AN IP RANGE:

As we mentioned above, whois can be used in other ways, not simply to look up a domain name. For example, we can use it to find out who a particular IP address belongs to:

whois 156.46.104.1
[rs.internic.net]
[No name] (NOMAD4-HST)

   Hostname: NOMAD.NET
   Address: 156.46.104.1
   System: IBM PC 486/66 running DOS/IPAD

   Record last updated on 03-Aug-95.
   Database last updated on 12-Sep-97 04:47:08 EDT.

This in itself doesn't give us much information (In fact, this information is out of date, and needs to be updated.) Ah well, another task, another day. What's more important than spam-fighting?

IP Blocks

(You can do an IP Block lookup automatically using the IP block lookup tool rather than using the following manual process.)

So, we can look for the owner of the license in question by stripping off the last digit of the IP address and replacing it with a zero. In this case we would do:

whois 156.46.104.0
[rs.internic.net]
No match for "156.46.104.0".

OK, so we didn't get a match. Still, someone owns the IP range in question. So, now we take off the last two IP "octets" and replace both of them with zeros. Thus:

whois 156.46.0.0
[rs.internic.net]
alpha dot net, corp. (NET-ALPHA)
   324 East Wisconsin Avenue, Suite 609
   Milwaukee WI, 53202

   Netname: ALPHA
   Netnumber: 156.46.0.0

   Coordinator:
      Chase, Tim  (TC15)  support@ALPHA.NET
      414-274-7040

   Domain System inverse mapping provided by:

   HOMER.ALPHA.NET              156.46.10.10
   HELEN.ALPHA.NET              156.46.10.20

   Record last updated on 10-Jan-96.
   Database last updated on 12-Sep-97 04:47:08 EDT.

Here is some useful information! We see that the actual IP range (often called a "Class C license") is owned by someone else entirely. In this case, it is owned by alpha dot net corp, in Milwaukee, Wisconsin. We have a contact name and e-mail address, and we have a telephone number. Remember, this will be an upstream provider for the spammer in question, and possibly not spammers themselves. We phrase our complaint accordingly, so as to not offend the good guys.

If that failed to get a result, we could simply keep replacing octets with zeros until we got the owner of an entire block of licenses, and again, we would have someone else to complain to.

The further away we get from the spammer, the less likely it is that we are dealing with spammer-friendly folks. If they get enough complaints, they MAY decide to take action, and we have all heard the phrase, "Shit rolls downhill." Eventually, someone has to take the heat and perhaps terminate the spammer. Keep this in mind.

USING WHOIS WHEN THERE ARE MULTIPLE RESULTS:

Sometimes, when we use whois, we get many responses, not just one. Here is an example:

whois mattocks
[rs.internic.net]
Mattocks E-mail Service (MATTOCKS-DOM)    MATTOCKS.COM
Mattocks, Bill (BM561)          bmattocks@COMP-SOL.COM (414)551-8088
Mattocks, Bill (BM1199)         bmattocks@COMP-SOL.COM (414)551-8088
Mattocks, Christopher (CM3732)  kolis@LVWEBMASTERS.COM  6024884305
Mattocks, Darryl (DM812)        darryl.mattocks@BOOKSHOP.CO.UK
Mattocks, Jeff (MJ100-ORG)      JeffMattocks@MSN.COM   (360) 896-8150

To single out one record, look it up with "!xxx", where xxx is the handle,
shown in parenthesis following the name, which comes first.

And sure enough, there are instructions right there as to how to narrow down your search. Simply type in an "!" followed by the information shown in the parenthesis. In my case, it would be:

whois '!BM561'

and that would bring up my information.

FOREIGN WHOIS SEARCHES:

What about domains located outside the US? Well, our information is a bit spotty there. There are equivalents of Internic outside of the US, and they work the same way. Some of them can be searched using the WHOIS tool, but just telling it to point itself at a different database. Other times, a search of the web using something like www.yahoo.com will bring you to a web page that will let you do a foreign whois search directly from that web page.

(List of foreign servers snipped - Sam Spade handles these for you)

That concludes the lesson for today. Please feel free to throw roses or brickbats, as you see fit. Permission is hereby granted for anyone who wishes to publish this information in any form, as long as it remains intact and attribution to the author is given. I maintain copyright and transfer all other rights to the public.

Best Regards,

Bill Mattocks, CIIU

Derived from an HTML translation by Marek Jedlinski of a usenet post by Bill Mattocks


Spam-tracking 104: a spammer unmasked; by Bill Mattocks

Spam-tracking 104:
a spammer unmasked

by Bill Mattocks

This is an actual case study of a spam that I received today and tracked to the source. It is intended as a lesson in spam-tracking for the uninitiated or the beginner in spam-tracking. It shows that with patience, all things are possible.

Are you sitting comfortably? Good, then I'll begin.

Today I got spam. That's nothing new, I get spam everyday. But this spam was from Wisconsin, and I happen to live in Wisconsin. I feel a powerful need to get rid of spam in Wisconsin. So, here is what happened and what I did about it.

This is the spam I got:

*****************************************************************************
>Received: from mail.tds.net (unverified [204.246.1.2]) by mail.comp-sol.com
> (EMWAC SMTPRS 0.83) with SMTP id <B0000040843@mail.comp-sol.com>;
> Mon, 06 Oct 1997 15:55:11 -0500
>Received: from Comp1 (mewi0-a10.midway.tds.net [204.246.12.107])
>	by mail.tds.net (8.8.5/8.8.5) with SMTP id PAA03860;
>	Mon, 6 Oct 1997 15:19:42 -0500 (CDT)
>Date: Mon, 6 Oct 1997 15:19:42 -0500 (CDT)
>Message-Id: <199710062019.PAA03860@mail.tds.net>
>From: webbs@tds.net
>Subject: Your Home And Family

>YOUR HOME AND FAMILY

>Now available,(Your Home and Family), the consumer guide everyone
>has been asking for.

>This guide is filled with information every household should be
>aware of. Protect yourself and your family, be informed of the
>real life events that can happen to you and your household.

>Read about wills and trusts (don’t let the government take
>everything)!

>Parents worst fears- (Drug Abuse, maybe its already there)! Be
>informed!

>Dealing with divorce “Get It Together” “Not The End”.

>Safeguards against rape....Don’t let it happen to you, worse yet
>a member of your family!

>Household: Don’t let your house get the better of you, TAKE
>CONTROL!

>This guide is packed full of important information that you will
>want to share with friends and other family members.

>This is “MUST HAVE INFORMATION”. Get this NOW!

>Send for your copy today! Here is how to order: Send check or
>money order for $29.95 (shipping and handling included in price)
>to:

>Affordable Services
>PO Box 352
>Medford, WI 54451

>PS: You won’t believe the startling information in the guide!
>Order an extra report for your friends and neighbors! Give
>yourself a little piece of mind.

*****************************************************************************

Normally, this is the most innocuous type of spam. It purports to sell a report, but it is not MLM, a pyramid scheme, or a chain letter.

The sender appears not to have hijacked a mail server to send the spam. The return address could even be legitimate, for all I know.

Therefore, it is not illegal on its face. Sending spam itself is legal, so it would appear that no laws were broken, except that I was unhappy over having gotten the spam in the first place.

So, I sent a letter of complaint to the postmaster at TDS.NET, letting them know that they are harboring a spammer. If they don't permit spamming, they may well terminate the begger. If I never get spam from him again, that should be the end of it, right?

Wrong.

I also did a little checking. Curious sort, am I. I used DejaNews to check out the city of Medford, WI (that's where I'm supposed to send the money for the report to, right)? Here is what I got back:

>1. 97/10/05 028 [email] (UCE) Your Home  news.admin.net-abus "Nasty Mama"

Ok, so it appears that Nasty Mama has also gotten this spam and has taken some action. But wait, there's more:

>10. 97/09/23 026 [email] Pyramid Sche#1/2 news.admin.net-abus Todd C. Lawson

Oh-ho! Pyramid scheme, eh? Well, let's just take a look!

*****************************************************************************
>Subject:      [email] Pyramid Scheme from newnorth.net (Your Free Report)
>From:         Todd C. Lawson <tlawson@amug.org>
>Date:         1997/09/23
>Message-Id:   <v03110703b04d9662ef4b@[204.62.193.226]>
>Newsgroups:   news.admin.net-abuse.sightings
>[More Headers]

>X-Reply-to: news.admin.net-abuse.email
>Abuse-spotted-in: mailbox tlawson@amug.org
>Abuse-Subject: Your Free Report
>Type-of-abuse: Unsolicited Email, Pyramid Scheme
>Description: Pyramid Scheme

[snip - VERY excellent information from Todd Lawson on what a pyramid scheme is, and why it is illegal. For a copy of his report, take a look at it. Use DejaNews and search for Todd Lawson.]

>Return-Path: webbs321@newnorth.net

Whups, not from TDS.NET, but from NEWNORTH.NET, which is another local ISP in rural Wisconsin. Ok, so maybe this guy got bounced based on Todd's complaint. Notice the similarity in user names, though (webbs vs. webbs321).

[snip rest of headers]

>Free Report

>Students! Profesionals!  Unemployed! Absolutely anyone can use this
>information to
>make cash anytime they want. Read and save this report to use time and time
>again.

[snip much pyramid stuff, we've all seen it.]

>This program has remained successful
>because of the
>HONESTY Integrity the participants.

[Well, not only does our unknown spammer send illegal pyramid schemes, but he is also a liar, as I will also show!]

[snip - more pyramid stuff]

>HERE IS THE LIST OF NAMES TO SEND TO:

>1. R.D.Haar, 1628 Hillcrest St. Mesquite, TX 75149 Fargo, USA

>2. James Shanahan, 2/16 Myola St., Mayfield 2304, Australia

>3. Diane Wicke,  PO box 32,  Jump River,  WI  54434,   USA

>4. Affordable Services,  PO Box 352,  Medford, WI  54451,   USA

[But wait - here is the man who sent me the UCE! ^^^^^^^ ]

>5. Scott Webster,  939 High Street #102,  Rib Lake, WI  54470,   USA

[And who could this be???]

>Mail $1.00 to each of the 5 names listed above. SEND CASH ONLY (Total
>investment:

[snip - more pyramid scheme]

>REMEMBER - THIS PROGRAM FAILS ONLY IF YOU ARE NOT HONEST
>-PLEASE!!  PLEASE  BE HONORABLE...IT DOES WORK! THANK YOU

[yet another exhortation to BE HONEST!!!]

*****************************************************************************

Ok, so now we know that webbs@TDS.NET sends UCE and pyramid scheme UCE. We could stop there and just mail a copy of this pyramid scheme to:

POSTMASTER
MEDFORD, WI 54451

and we'd be done with it. But I'm just a curious guy, so I took the very last step in identifying this spammer. I called the US Post Office in Medford, WI at (715) 748-3981. Remember, if the holder of a US Post Office Box lists their PO Box as being used for business, the information is open to the public. If they check off the little box that says that they are NOT doing business with the public, then you can't get the info, but then they are committing perjury (PO Box applications are legal documents). It seems our spammer DID want to be just a little bit honest though, because the post office told me who he is (drum roll, please):

Scott's Affordable Services
939 High Street
# 102
Rib Lake, WI 54470

Oh gee. Seems like Mister Scott Webster from our pyramid scheme above and Affordable Services from the same list are indeed the same person. On top of that, I would venture a guess to say that webbs and webbs321 both mean Scott Webster, huh? So, same person all the way around. He just could not restrain himself from cheating on his very own pyramid scheme, the one he warns people NOT to cheat on. Shame, shame, Mr. Webster.

Now, I complain to his ISP. I print a copy of the pyramid scheme that was previously posted to news.admin.net-abuse.sightings by Todd Lawson, and I send it to the postmaster at Rib Lake, WI, and Medford, WI.

Our nasty little spammer is going to stop bouncing from ISP to ISP, because he is going to jail.

Thus endeth the lesson.

Best Regards,

Bill Mattocks, CIIU

Derived from an HTML translation by Marek Jedlinski of a usenet post by Bill Mattocks


Using Sam Spade

Showing full headers

If you don't see a Received: line in your email headers your mail client is hiding some headers from you. In order to find the source of an email you need to see the full headers

All mail readers have some way of showing fullheaders:

Eudora Light before V3
Select Tools, Options..., then Fonts & Display then Show all headers (even the ugly ones)
Eudora Light (V3+)
Press the BLAH button on the incoming mail message
Netscape 3
Select Options, Show Headers, then All


Internet Protocol Addressing and Domain Name Service

Internet Protocol Addressing

(This is a pruned version of the full document)

Each machine connected to an IP network (such as the Internet) is addressed using a unique 32 bit number, the IP address.

Many machines will have more than one IP address - for example a machine running virtual websites will have an IP address for each website they host.

Other times a pool of IP addresses is shared between a number of machines - eg on a dynamic-IP dialup connection your machine will be allocated a different IP address each time you connect.

These addresses are usually written in Dotted Quad notation, as a series of four 8 bit numbers, written in decimal and separated by periods. For example 151.196.75.10

Each number is in the range 0 to 255 - so if you ever see something that looks like an IP address with numbers outside those ranges it's not a real address.

The leftmost number is the most significant, and the rightmost the least.

So 151.196.75.10 and 151.196.75.11 are right next-door to each other whilst 151.196.75.10 and 152.196.75.10 are completely unrelated.

IP Address allocation

Traditionally IP addresses were allocated to companies and ISPs in blocks.

A Class A Address Block or, less formally, an A Block is a block of 16,000,000 or so (2^24) addresses from X.0.0.0 to X.255.255.255, where 0 < X < 127.

So the entire 9.0.0.0 - 9.255.255.255 range of addresses is the A Block owned by IBM.

A B Block is a block of 65,000 or so (2^16) addresses from X.Y.0.0 to X.Y.255.255, where 127 < X < 192 and 0 <= Y < 256

A C Block is a block of 256 addresses from X.Y.Z.0 to X.Y.Z.255 where 191 < X < 224, 0 <= Y,Z <256

(There are also D and E class addresses allocated in the 224-255 range - these are reserved for multicast and experimental applications - you'll never see them in practice)

Traditional blocks are often described using the first address in the block, eg IBM own A Block 9.0.0.0 and Cyberpromo own C Block 205.199.2.0. Other times they may be described using just the constant prefix, eg net 9 for IBM or net 205.199.2 for CyberPromo

[You'll often hear any address range from X.Y.Z.0 to X.Y.Z.255 called a C Block, even though it technically isn't unless 191 < X < 224]

CIDR Allocation

Recently things have changed. IP addresses are in short supply and routers have become more sophisticated, so it's now usual to allocate blocks of addresses on pretty much any bit boundary. You'll often see blocks of 64 addresses for instance, such as 151.196.75.128 to 151.196.75.191

A common way of naming these blocks is CIDR syntax, this is the initial constant prefix and the length in bits.

So 151.196.75.0 to 151.196.75.255 might be described as 151.196.75/24

and 151.196.75.128 to 151.196.75.191 might be described as 151.196.75.128/26

But who's in charge of this address block?

To find out who administers a block of addresses you can use theIP Block tool to query the internic database. This is sometimes wildly out of date, but can be a good start.

Special Cases

There are some special ranges of addresses reserved for special uses.

Loopback

The 127.*.*.* block is reserved for local loopback - so these addresses will always point back to your own machine.

The canonical loopback address is 127.0.0.1

Private networks

Some blocks of addresses are allocated for private networks - packets from these machines should be dropped by most routers. Why is this useful? If you want to setup a private network you don't need to use up any of the scarce allocation of 'real' IP addresses. So you need to make up your own addressing scheme to use internally.

As long as you're not connected to the internet in any way you could use any scheme you wanted. But what if you have a gateway machine that lets you e.g. send mail to and from the internet? If you chose an arbitrary IP address range for your local network and the packets leaked through the gateway onto the internet they'd end up going to the Real owner of those addresses, probably fubaring their system and provkoking stern 'phone calls.

This has happened, even within huge multinational corporations who didn't follow the rules and chose arbitrary addresses for their internal network.

So there are ranges of addresses allocated as private addresses. You can use these quite safely, as everyone's routers are told to just throw packets to or from these addresses away. So if your packets escape they'll be deleted.

These private address blocks are:

  • 10.0.0.0 - 10.255.255.255
  • 172.16.0.0 - 172.31.255.255
  • 192.168.0.0 - 192.168.255.255

If you see one of these addresses in a received line it means the email has been forwarded around an internal network before being gatewayed to the internet proper.

So far, so froody.

There are some IP addresses in each block reserved for broadcast and other obscure stuff. Check the RFCs if you're really interested.

Email to an IP address

Incidentally, if you want to send email to a machine and you know the machines IP address you can send it to user@[w.x.y.z]

So, just for the sake of example, if you were to put this HTML tag:

<A HREF="mailto:postmaster@[127.0.0.1]"></A>

in a web page, and someone were to scan your webpage for email addresses and then try to send spam to them they'd end up sending a copy to postmaster at the machine 127.0.0.1. As we said earlier 127.0.0.1 is always your own machine, so this would make the spammer spam their own system administrator....

Name resolution

IP addresses work pretty well, but they're not as memorable as machine names. So we need some way to map names to addresses (and ideally back again).

Domain Name Service

DNS is a distributed system. The end user doesn't really care about this, but in case you're interested here's how it works.

You try and access http://www.blighty.com. Netscape wakes up and asks Windows what IP address www.blighty.com maps to.

Windows then sends a request to your local nameserver, usually the nameserver of your ISP. If someone else has looked up the address recently the nameserver might already know the answer. If not it realizes that it doesn't know, and works out who might know.

Your ISPs nameserver then contacts that nameserver - if it knows it answers. If not, it works out who might know.... you get the idea.

Finally the result gets forwarded back to your local nameserver, which caches the result so it can use it again later and passes the answer back to your system - 151.196.75.10.

[In reality it's a bit more formal than that, with 'zones of authority' rather than guesswork to find out who might know the answer]

DNS lookups

If you want to query your DNS server directly you can use the DNS tool

Reverse lookup

Finding the hostname given the IP address is very useful. If you're tracing spam you need the domainname to be able to find whois information.

Sometimes you can just use the DNS tool on an IP address, and it'll give you the hostname. Sometimes it won't be able to find a hostname

Just because a host has forward DNS from name to address there's no guarantee or requirement for it to have reverse DNS from address to name. Many sites do, many sites don't.

If there's no reverse DNS you need to resort to guerrilla approaches. If there's a web site that's a good bet. Do a view source to look at the HTML source, particularly for forms and mailto links.

Sometimes telnetting to the machine will give a banner identifying the machine. Or telnetting to other ports on the machine (25, 110, 119) can sometimes give a banner. Then you can use forward DNS to confirm that the address maps back to the right IP.

The port scan tool can scan a range of ports on a machine, to see which are providing services. Then you can telnet to each one in turn to see if any leak information.

What if the site is being coy, and trying to hide their domainname? Most virtual web-hosting companies require customers to have a domain name, but if it's not used anywhere and the website is advertised using it's IP address rather than domain name it's hard to find.

On some virtual web servers accessing http://w.x.y.z/stats or http://w.x.y.z/logs triggers a redirect that can give you the name.

Advanced DNS

DNS has all sorts of good stuff in it, not just the address-name mappings. You can get at this with the dig tool.

These are some of the things it can tell you:

A
The Address of a hostname. There's only one A record for each host.
NS
The authoritative nameserver for a domain
MX
The mail exchanger for the domain. For example, there is no such machine as demon.net, yet you can send email to user@demon.net. The MX record tells the mail system to send mail for user@demon.net to user@relay-1.mail.demon.net instead.
CNAME
An alias for a machine. A number of different names may resolve to the same IP address. A CNAME entry for a domain points you at the real name of the system.
HINFO
Hardware and software used by the host
RP
The responsible person for a domain
ANY
All records available

There are around 40 more, but these are the most common.

You can also ask for a complete Zone Transfer from a nameserver. This contains all the records it has for a given domain. If there's no reverse DNS setup this can be the only way of getting a full list of the machines within a domain. You can only do a zone transfer from the name server that is authoritative for a domain, so you need to query your local nameserver to find an authorative server for a domain before doing a zone transfer

References

  • Internet Protocol
    • RFC791
    • RFC1122
    • A more technical tutorial
  • Private addresses:
    • RFC1918
  • DNS
    • A good DNS overview
    • Lots of DNS resources
    • Linux DNS Howto
  • O'Reilly books
    • TCP/IP Network Administration
    • DNS and BIND
    • Essential Windows NT System Administration
    • Linux Network Administrator's Guide


Regular expressions

(This documentation is derived from Tom Lords documentation for the Free Software Foundation regular expression library)

Copyright (C) 1996 Tom Lord Berkeley, CA USA

Permission to use, copy, modify, distribute, and sell this documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation.

Posix Basic Regular Expressions

The Posix Basic Regular Expression language is a notation for describing textual patterns. Regexps are typically used by comparing them to a string to see if that string matches the pattern, or by searching within a string for a substring that matches.

This chapter introduces the Posix regexp notation. This is not a formal or precise definition of Posix regexps -- it is an intuitive and hopefully expository description of them.

An Introduction to Regexps

In the simplest cases, a regexp is just a literal string that must match exactly. For example, the pattern:

regexp

matches the string "regexp" and no others.

Some characters have a special meaning when they occur in a regexp. They aren't matched literally as in the previous example, but instead denote a more general pattern. For example, the character * is used to indicate that the preceeding element of a regexp may be repeated 0, 1, or more times. In the pattern:

smooo*th

the * indicates that the preceeding o can be repeated 0 or more times. So the pattern matches:

smooth
smoooth
smooooth
smoooooth
...

Suppose you want to write a pattern that literally matches a special character like * -- in other words, you don't want to * to indicate a permissible repetition, but to match * literally. This is accomplished by quoting the special character with a backslash. The pattern:

smoo\*th

matches the string:

smoo*th

and no other strings.

In seven cases, the pattern is reversed -- a backslash makes the character special instead of making a special character normal. The characters +, ?, |, (, and ) are normal but the sequences \+, \?, \|, \(, \), \{, and \} are special (their meaning is described later).

The remaining sections of this chapter introduce and explain the various special characters that can occur in regexps.

Literal Regexps

A literal regexp is a string which contains no special characters. A literal regexp matches an identical string, but no other characters. For example:

literally

matches

literally

and nothing else.

Generally, whitespace characters, numbers, and letters are not special. Some punctuation characters are special and some are not (the syntax summary at the end of this chapter makes a convenient reference for which characters are special and which aren't).

Character Sets

This section introduces the special characters . and [.

. matches any character except the NULL character. For example:

p.ck

matches

pick
pack
puck
pbck
pcck
p.ck

...

[ begins a character set. A character set is similar to . in that it matches not a single, literal character, but any of a set of characters. [ is different from . in that with [, you define the set of characters explicitly.

There are three basic forms a character set can take.

In the first form, the character set is spelled out:

[<cset-spec>]	-- every character in <cset-spec> is in the set.

In the second form, the character set indicated is the negation of a character set is explicitly spelled out:

[^<cset-spec>]	-- every character *not* in <cset-spec> is in the set.

A <cset-spec> is more or less an explicit enumeration of a set of characters. It can be written as a string of individual characters:

[aeiou]

or as a range of characters:

[0-9]

These two forms can be mixed:

[A-za-z0-9_$]

Note that special regexp characters (such as *) are not special within a character set. -, as illustrated above, is special, except, as illustrated below, when it is the first character mentioned.

This is a four-character set:

[-+*/]

The third form of a character set makes use of a pre-defined "character class":

[[:class-name:]] -- every character described by class-name is in the set.

The supported character classes are:

alnum	- the set of alpha-numeric characters
alpha	- the set of alphabetic characters
blank	- tab and space
cntrl	- the control characters
digit	- decimal digits
graph	- all printable characters except space
lower	- lower case letters
print	- the "printable" characters
punct	- punctuation
space	- whitespace characters
upper	- upper case letters
xdigit	- hexidecimal digits

Finally, character class sets can also be inverted:

[^[:space:]] - all non-whitespace characters

Character sets can be used in a regular expression anywhere a literal character can.

Subexpressions

A subexpression is a regular expression enclosed in \( and \). A subexpression can be used anywhere a single character or character set can be used.

Subexpressions are useful for grouping regexp constructs. For example, the repeat operator, *, usually applies to just the preceeding character. Recall that:

smooo*th

matches

smooth
smoooth
...

Using a subexpression, we can apply * to a longer string:

banan\(an\)*a

matches

banana
bananana
banananana
...

Subexpressions also have a special meaning with regard to backreferences and substitutions (see See Backreferences, Extractions and Substitutions).

Repeated Subexpressions

* is the repeat operator. It applies to the preceeding character, character set, subexpression or backreference. It indicates that the preceeding element can be matched 0 or more times:

bana\(na\)*

matches

bana
banana
bananana
banananana
...

\+ is similar to * except that \+ requires the preceeding element to be matched at least once. So while:

bana\(na\)*

matches

bana

bana(na\)\+

does not. Both match

banana
bananana
banananana
...

Thus, bana\(na\)+ is short-hand for banana\(na\)*.

Optional Subexpressions

\? indicates that the preceeding character, character set, or subexpression is optional. It is permitted to match, or to be skipped:

CSNY\?

matches both

CSN

and

CSNY

Counted Subexpressions

An interval expression, \{m,n\} where m and n are non-negative integers with n >= m, applies to the preceeding character, character set, subexpression or backreference. It indicates that the preceeding element must match at least m times and may match as many as n times.

For example:

c\([ad]\)\{1,4\}

matches

car
cdr
caar
cdar
...
caaar
cdaar
...
cadddr
cddddr

Alternative Subexpressions

An alternative is written:

regexp-1\|regexp-2\|regexp-3\|...

It matches anything matched by some regexp-n. For example:

Crosby, Stills, \(and Nash\|Nash, and Young\)

matches

Crosby, Stills, and Nash

and

Crosby, Stills, Nash, and Young

Backreferences, Extractions and Substitutions

A backreference is written \n where n is some single digit other than 0. To be a valid backreference, there must be at least n parenthesized subexpressions in the pattern prior to the backreference.

A backreference matches a literal copy of whatever was matched by the corresponding subexpression. For example,

\(.*\)-\1

matches:

go-go
ha-ha
wakka-wakka
...

In some applications, subexpressions are used to extract substrings. For example, Emacs has the functions match-beginnning and match-end which report the positions of strings matched by subexpressions. These functions use the same numbering scheme for subexpressions as backreferences, with the additional rule that subexpression 0 is defined to be the whole regexp.

In some applications, subexpressions are used in string substitution. This again uses the backreference numbering scheme. For example, this sed command:

s/From:.*<\(.*\)>/To: \1/

first matches the line:

From: Joe Schmoe <schmoe@uspringfield.edu>

when it does, subexpression 1 matches "schmoe@uspringfield.edu". The command replaces the matched line with "To: \1" after doing subexpression substitution on it to get:

To: schmoe@uspringfield.edu

A Summary of Regexp Syntax

In summary, regexps can be:

abcd -- matching a string literally

. -- matching everything except NULL

[a-z_?], ^[a-z_?], [[:alpha:]] and [^[:alpha:]] -- matching character sets

\(subexp\) -- grouping an expression into a subexpression.

\n -- match a copy of whatever was matched by the nth subexpression.

The following special characters and sequences can be applied to a character, character set, subexpression, or backreference:

* -- repeat the preceeding element 0 or more times.

\+ -- repeat the preceeding element 1 or more times.

\? -- match the preceeding element 0 or 1 time.

{m,n} -- match the preceeding element at least m, and as many as n times.

regexp-1\|regexp-2\|.. -- match any regexp-n.

A special character, like . or * can be made into a literal character by prefixing it with \.

A special sequence, like \+ or \? can be made into a literal character by dropping the \.

Ambiguous Patterns

Sometimes a regular expression appears to be ambiguous. For example, suppose we compare the pattern:

begin\|beginning

to the string

beginning

either just the first 5 characters will match, or the whole string will match.

In every case like this, the longer match is preferred. The whole string will match.

Sometimes there is ambiguity not about how many characters to match, but where the subexpressions occur within the match. This can effect extraction functions like Emacs' match-beginning or rewrite functions like sed's s command. For example, consider matching the pattern:

b\(\[^q]*\)\(ing\)?

against the string

beginning

One possibility is that the first subexpression matches "eginning" and the second is skipped. Another possibility is that the first subexpression matches "eginn" and the second matches "ing".

The rule is that consistant with matching as many characters as possible, the length of lower numbered subexpressions is maximized in preference to maximizing the length of later subexpressions.

In the case of the above example, the two possible matches are equal in overall length. Therefore, it comes down to maximizing the lower-numbered subexpression, \1. The correct answer is that \1 matches "eginning" and \2 is skipped.

Complications in Subexpression Matching

Sometimes a subexpression matches a substring of no characters. This happens when `f\(o*\)' matches the string `fum'. (It really matches just the `f'.) In this case, both of the offsets identify the point in the string where the null substring was found. In this example, the offsets are both 1.

Sometimes the entire regular expression can match without using some of its subexpressions at all--for example, when `ba\(na\)*' matches the string `ba', the parenthetical subexpression is not used. When this happens, regexec stores -1 in both fields of the element for that subexpression.

Sometimes matching the entire regular expression can match a particular subexpression more than once--for example, when `ba\(na\)*' matches the string `bananana', the parenthetical subexpression matches three times. When this happens, regexec usually stores the offsets of the last part of the string that matched the subexpression. In the case of `bananana', these offsets are 6 and 8.

But the last match is not always the one that is chosen. It's more accurate to say that the last opportunity to match is the one that takes precedence. What this means is that when one subexpression appears within another, then the results reported for the inner subexpression reflect whatever happened on the last match of the outer subexpression. For an example, consider `\(ba\(na\)*s \)*' matching the string `bananas bas '. The last time the inner expression actually matches is near the end of the first word. But it is considered again in the second word, and fails to match there. regexec reports nonuse of the "na" subexpression.

Another place where this rule applies is when the regular expression `\(ba\(na\)*s \|nefer\(ti\)* \)*' matches `bananas nefertiti'. The "na" subexpression does match in the first word, but it doesn't match in the second word because the other alternative is used there. Once again, the second repetition of the outer subexpression overrides the first, and within that second repetition, the "na" subexpression is not used. So regexec reports nonuse of the "na" subexpression.


Traceroute, yo ho ho and a bottle of rum and all that

Traceroute

All information is transferred over the 'net by pirates1.

When you send your data somewhere, what *really* happens is that one of your pirates picks up that data, works out where he's going and strides purposefully off down the information superhighway.

Normal pirates are fit and strong. They can walk nearly any distance. Occasionally, though, you'll send them to somewhere that just doesn't exist. Then they may realise they can't get any closer, in which case they'll just curl up and die2.

Or they might end up walking in circles again and again, never actually getting any further, until they collapse through exhaustion and die3.

Normally you'll just send your pirates out and if they don't make it to the far end that's just too bad.

Sometimes though, you really want to know whether your pirate made it, or if he didn't, how far did he get.

For this you need a more sophisticated way of sending information - Pirates-with-Parrots4. You put a highly trained Norwegian Homing Parrot5 on the shoulder of your pirate. It's not dead, just sleepin', so when your pirate dies his parrot wakes up, takes a look around, works out where he is and flies back home to you. Then the parrot tells you how far your pirate got, and where he was when he died.

You save up all, your old, weak, feeble pirates for use with the traceroute command.

Traceroute starts off with the most feeble of feeble pirates.

You tell them to go to the remote system you're trying to traceroute. Your pirate works out where he needs to go, heads boldly off in the right direction, and promptly drops dead after having taken one step.

His parrot then flies home and tells you the name of the system one step in that direction.

Next you pick out a stronger pirate and try the same thing. He works out where he's going, finds the same route as the first pirate, heads boldly off and drops dead after two steps. Again, his parrot wakes up, flies home and tells you the name of the system two steps away.

You keep doing this with progressively stronger pirates and plot the sequence of hosts you go through to get to the far end.

(In practice you send three pirates on each step, rather than one, 'cos some get lost sometimes. And you measure the time it takes for the pirate to get there and the parrot to get back to measure how treacherous the route is in that direction).

If someone wants to prevent you tracerouting into an area they could do one of two things. One would be to catch any incoming pirates and feed them to the crocodile7, but that would mean they couldn't use pirates for useful purposes. So instead they'll usually surround their demesne with teams of crack parrot hunters.

One of your feeble pirates-with-parrots will die just outside their demesne, and you'll receive his parrot back. Your next, slightly stronger pirate-with-a-parrot will take one step further, inside the ring of hunters. He'll die, of exhaustion, just as you expected him to.

His parrot will wake up, work out where he is and start heading for home.

'PULL!'

'Bang! Bang! Bangeddy-bang-bang!'

'Squawk! Mayday! Mayday! Squawk! Squ.....'

There's a hail of lead and nothing left of poor polly but a small cloud of green feathers, gently fluttering to earth.

Each pirate-with-a-parrot you send after this will get further and further towards his destination, but every returning parrot will be turned into polly-puree so you won't know how far he gets.

That, gentle readers, is why you can't traceroute into the more technically savvy, spam-friendly providers8.

Ping also uses a Pirate-with-Parrot protocol, so if you can't traceroute there you can't ping there either.

Apart from ping and traceroute no user level protocol uses parrots, so just because you can't ping or traceroute a host doesn't mean other protocols won't work.

  1. aka TCP or UDP packets.
  2. When there's no route to the remote host.
  3. A quick-n-dirty way of disconnecting a host is to make the connecting router point back to itself - this makes the packets run around in a circle until they expire. You'll sometimes see this in traceroutes which after a certain point keep repeating a single host, or a repeating sequence of hosts until they give up9.
  4. aka UDP (User Datagram Protocol) datagrams
  5. aka ICMP (Internet Control Message Protocol) Destination Unreachable packets.
  6. Oi! Stop reading footnotes that haven't been referenced. You've played HHGTTG, haven't you?
  7. Tick...Tock...Tick...Tock... "He's behind you!"
  8. It's just a metaphor, right? I'm not really trying to tell you that information is sent around with pirates and birds, am I? Take a look at RFC1149
  9. You'll sometimes see that if you try and traceroute to a dynamic IP dialup system that isn't connected at the moment too. The terminal servers route in a ring, each terminal server going 'If I have this address on one of my dialups I'll send the packet there, else I'll send it to the terminal server on my left'.


Decrypting Javascript

Background

For a while now spammers have been using obfuscated URLs (eg http://microsoft.com@3463815505/) in an attempt to hide where their webpages are hosted.

Many people now know how to find the real address hidden behind these URLs, either by hand or by using canned tools.

The next step was using throwaway webpages on providers such as Angelfire, with the results of the signup form being emailed to an account elsewhere (often a throwaway address or forwarding address) either using an ACTION="mailto:throwaway@yahoo.com" or using an public cgi script at another site (ACTION="http://insecure.tld/cgi-bin/formmail.cgi" combined with <INPUT TYPE="hidden" NAME="recipient" VALUE="throwaway@yahoo.com">).

If you look at the source code, either with View Source from your browser, or using a canned tool you can see the recipient.

So now... the spammers are using tools like HayWyre to ship the contents of the webpage in an encoded format, then use javascript running on the recipients browser to decode it. This means that the HTML source can't be seen easily - as View Source from a browser will just show the encoded form.

Here is an example of the obfuscated source code. Here is the executable javascript version - you'll need to have javascript enabled in your browser.

Browser Specific Solutions

All of these require you to run javascript on your browser, so if there's anything 'hostile' in there, it'll run. Generally, though, javascript can't do much worse than crash your browser or force you to kill the process by hand

Internet Explorer 5

Microsoft have a host of useful plugins for IE5 at http://www.microsoft.com/Windows/ie/WebAccess/default.ASP, including the View Partial Source tool as part of their Web Developer Accesories pack. Grab it and install it (while you're there the web accesories and the AltaVista tools are handy to have too).

Once it's installed you can load the page then select the whole page or a section of it, right-click and select View Partial Source to see the HTML for the selection.

Netscape Navigator 3/4/5

Navigator is documented to show the HTML source generated by javascript document.write() calls. I have once managed to persuade it to do this properly, so it is possible, but there seem to be some strange bugs in its caching of data that makes it hard to do consistently.

Edit the source

If you download the source, then edit it locally you can persuade the javascript to show you the HTML source produced.

There are lots of ways to do this, but this one seems pretty portable.

Edit the source and look for document.write(foo) for some foo. Replace this with document.write(foo.split("<").join("<br>&lt;"))

You'll normally end up with:

function d(ber) {document.write(c(alf,ber).split("<").join("<br>&lt;"));}

Load the file into your browser, and you'll see the source.


The Wildmat Format

From the Inn documentation set and the NNTP extensions internet draft

The WILDMAT format was first developed by Rich Salz based on the format used in the UNIX "find" command to articulate file names. It was developed to provide a uniform mechanism for matching patterns in the same manner that the UNIX shell matches filenames. Patterns are implicitly anchored at the beginning and end of each string when testing for a match. There are five pattern-matching operations other than a strict one-to-one match between the pattern and the source to be checked for a match.

The pattern is interpreted as follows:

\x | Turns off the special meaning of x and matches it directly; this is used mostly before a question mark or asterisk, and is not special inside square brackets.
? | Matches any single character.
* | Matches any sequence of zero or more characters.
[x...y] | Matches any single character specified by the set x...y. A minus sign may be used to indicate a range of characters. That is, [0-5abc] is a shorthand for [012345abc]. More than one range may appear inside a character set; [0-9a-zA-Z._] matches almost all of the legal characters for a host name. The close bracket, ], may be used if it is the first character in the set. The minus sign, -, may be used if it is either the first or last character in the set.
[^x...y] | This matches any character not in the set x...y, which is interpreted as described above. For example, [^]-] matches any character other than a close bracket or minus sign.

Examples

*news.admin.net-abuse.* | the usual form - matches all newsgroups in the news.admin.net-abuse hierarchy
[^]-] | matches any single character other than a close square bracket or a minus sign/dash.
*bdc | matches any string that ends with the string "bdc" including the string "bdc" (without quotes).
[0-9a-zA-Z] | matches any single printable alphanumeric ASCII character.
a??d | matches any four character string which begins with a and ends with d.


Tip of the Day

  1. HTH: Happy to help or Hope that helped
  2. Never try to outstubborn a cat
  3. AFAIK: As far as I know
  4. HAND: Have a nice day
  5. RTFM: Read the fine manual
  6. Your enemy is never a villain in his own eyes. Keep this in mind, it may offer a way to make him your friend. If not, you can kill him without hate, and quickly.
  7. RTFF: Read the fine FAQ
  8. FAQ: Frequently Asked Questions(http://www.faqs.org/faqs/)
  9. Never eat yellow snow
  10. The term 'spam' is a reference to the Monty Python sketch (Vikings, remember?)
  11. UCE: Unsolicited Commercial Email
  12. IP addresses in Received lines are a good way to filter email
  13. Never underestimate the power of human stupidity
  14. NoCeM: A way of letting others filter your newsfeed (http://www.cm.org/)
  15. Avoid making irrevocable decisions while tired or hungry.
  16. BI: The Breidbart Index, a measure of the breadth of a usenet spam
  17. Afterburner: The abuse guy at erols.net. One of the most rabid anti-spam people around. Erols spammers get deep-fried, extra crispy. One of the Good Guys.
  18. CAUCE: The Coalition against Unsolicited Commercial Email (http://www.cauce.org/)
  19. RFC: Request For Comments, the 'laws' of the Internet. (http://www.faqs.org/rfcs/)
  20. postmaster: The person responsible for email problems at a site. All sites must have a 'postmaster@' email address
  21. abuse: Many sites have an 'abuse@' address for handling email and usenet abuse. It isn't required.
  22. Moving parts in rubbing contact require lubrication to avoid excessive wear. Honorifics and formal politeness provide lubrication where people rub together. Often the very young, the untraveled, the naive, the unsophisticated deplore these formalities as 'empty', 'meaningless', or 'dishonest' and scorn to use them. No matter how 'pure' their motives, they thereby throw sand into machinery that does not work too well at best.
  23. UBE: Unsolicited Bulk Email
  24. procmail: A UNIX program for filtering incoming email
  25. Be wary of strong drink. It can make you shoot at tax collectors, and miss.
  26. ignorebot: A program that handles email sent to an abuse@ address by sending a soothing reply, and deleting the original complaint
  27. How you behave towards cats here below determines your status in Heaven
  28. LART: Luser Attitude Readjustment Tool. Baseball bats are popular.
  29. Cabal: There is no cabal
  30. ACHTUNG! ALLES LOOKENSPEEPERS! Das computermachine ist nicht fuer gefingerpoken und mittengrabben. Ist easy schnappen der springenwerk, blowenfusen und poppencorken mit spitzensparken. Ist nicht fuer gewerken bei das dumpkopfen. Das rubbernecken sichtseeren keepen das cotten-pickenen hans in das pockets muss; relaxen und watchen das blinkenlichten.
  31. Third party relay: To bounce an email message through an innocent third party's mailserver
  32. Tim Skirvin: One of the main usenet spam-cancellors. One of the Good Guys.
  33. S.P.U.T.U.M. The SubGenius Police, Usenet Tactical Unit (Mobile). These are the antispam SWAT team of the Church of the SubGenius
  34. Church of the SubGenius: A mutant offshoot of Discordianism. Much of SubGenius theory is concerned with the acquisition of the mythical substance or quality of slack
  35. Chris Lewis: One of the main usenet spam-cancellors. One of the Good Guys.
  36. IP Address: Unique identifier for a machine. Of the form a.b.c.d where each of a,b,c,d is a number >=0 and <256. If it's not like that, it's forged.
  37. emailed: embossed (with a raised pattern) or arranged in a net work, from the French 'emaillure', network
  38. Flame: To post an email or usenet message intended to insult and provoke.
  39. Fnord
  40. Backbone: One of the companies who provide the main backbone of the internet. An internet provider who doesn't buy bandwidth from anyone else.
  41. AGIS: Apex Global Internet Services. A rogue backbone provider. The source of much spam. Bouncing any email that's ever passed through an AGIS machine is a good way of reducing spam
  42. IMHO: In My Humble Opinion
  43. Kook: An extremely offensive, irrational, trolling usenetter. It's easy to troll, it takes great persistence to become a Kook.
  44. KotM: Kook of the Month
  45. Never appeal to a man's 'better nature.' He may not have one. Invoking his self interest gives you more leverage.
  46. dialup: A non-permanent connection to the 'net, typically over a 'phone line or ISDN line. Also known as a PoP (Point of Presence)
  47. mailbomb: To send someone hundreds of emails, with the intent of crashing their mail system. Don't do this. It's bad.
  48. killfile: A way of filtering out usenet posts from a particular poster. Real newsreaders have these.
  49. PGP: Pretty Good Privacy. A program for encrypting email and for unforgably signing email and usenet posts. (in the US and elsewhere)
  50. nastygram: A disapproving email, typically criticising anothers breach of netiquette
  51. ping: Packet INternet Groper
  52. dig: Domain Internet Groper
  53. regexp: Regular Expression, a powerful type of wildcard pattern. Used for pattern matching in procmail, amongst many other places
  54. You can have peace. Or you can have freedom. Don't ever count on having both at once.
  55. cancel-bot: A program that automatically cancels usenet posts. Some of these are good, cancelling spam and mis-posted binaries. Others are rogue cancelbots, used to disrupt newsgroups
  56. Spelling Flame: A usenet posting pointedly correcting the spelling of a previous article. If you make one of these, you've lost the argument.
  57. BTW: By The Way
  58. FWIW: For What It's Worth
  59. FYI: For Your Information
  60. LOL: Laughs Out Loud
  61. ROTFL: Rolling On The Floor, Laughing
  62. GoA,T: Go Away, Troll
  63. Troll: v. To post an inflammatory article in the hope of provoking a response. From the angling term. Troll n. One who Trolls. The best thing to do to a troll is to ignore it.
  64. Munge: To alter your email address, making it unusable. Don't do this. If you must do this, munge the bit to the right of the '@' sign, not the left.
  65. Down, not across
  66. /dev/null: The unix black-hole. Anything sent here is never seen again....
  67. The answer to any question starting 'Why don't they....' is almost always, 'Money'
  68. Whackamole: Spam being sent from a series of throwaway dialup accounts


Spam?

Prehistory

The history of the term Spam starts with episode 25 of Monty Python's Flying Circus, and the Spam Sketch in which a group of Vikings sing the word Spam over and over, so loudly that no-one else can communicate

After a short detour through MUDs, where it was used to describe someone shouting the same thing repeatedly, stopping any interaction between other people, it came to usenet.

Usenet spam

On usenet the term spam is used to refer to the practice of posting an article, often an advert for a dubious porn site or a ponzi scam, many, many times. This might be many times to one group or more usually it'll be posted to a lot of groups.

More precise terms for usenet spam are Excessive Crossposting (ECP) - crossposting the same article to many groups - or Excessive Multiposting (EMP) - posting substantively the same article many times, to each group individually

Most usenet spamming is a mix of ECP and EMP - the spam will be crossposted to many groups, many times

Usenet spam is automatically detected and cancelled by cancelbots, but because of the way usenet propagates some percentage of the spam will make it's way through to readers before the cancels catch them

Many sections of usenet have been turned into wastelands - whole hierarchies have been so deluged by spam that it's impossible to use them. The members of the groups have left, and there's nothing there but spam.

Around 80% of usenet traffic is caused by spam

What can be done?

Killfiles. All real newsreaders have a killfile - this is a way of filtering out posts based on subject, poster or a number of other things. These are good for filtering out the background noise and occasional spam in an otherwise good newsgroup. Web browsers that claim to be newsreaders seldom have usable killfile features (Under Win95/NT Forte Agent and Anawave Gravity are two commonly used newsreaders)

NoCeM is a way to allow someone else you trust to filter out articles for you. At the time of writing no Windows client software is available, but it is possible for your news adminstrator to use NoCeMs to delete spam from the server by installing NoCeM on spool software

Server filtering. It's possible to detect and delete the huge majority of usenet spam using server filters. Spam Hippo is a filter from Zippo news, which they make freely available to ISPs who wish to run it

Shunning. At any one time the majority of spam comes from a few sites. By shunning them, refusing all articles from them the amount of spam drops

Hunt the perpetrators down. Complain to their upstream provider. Repeatedly. Get them shut down. Get the servers, web pages and email addresses of the advertisers yanked. Report the illegal schemes to the local police.

Email spam

The 'correct' term for email spam is Unsolicited Bulk Email (UBE), though you'll see the term Unsolicited Commercial Email (UCE) used more often.

Spam is popular amongst the scammers, the multi-level marketing crowd and the porn pushers because it costs them so little to send. This is because they send it by stealing the resources of others.

There are three main flavours of email spam

  1. Spam sent by an ordinary customer of an ISP, sent via his ISPs mailserver, usually with minimal forging of the headers. This tends to be sent by newbie spammers. If they're slapped down by their ISP they may decide spamming is bad, or they may just get more sophisticated...
  2. Spam sent using spamware - programs specifically designed to send huge amounts of email (up to 100,000 emails an hour) over an ordinary dialup internet connection. This software is designed to steal service from an innocent third party by relaying email through their server. It's also designed to forge the email headers to deflect complaints away from the perpetrator, either towards the third-party or towards yet another innocent bystander. The load this puts on the third-party server can bring an ISP down for days
  3. 'Professional' spamhauses. These are companies setup purely to commit theft and fraud. They have permanent internet connections, or sometimes have their servers in the premises of other crooked service providers. They don't usually spam to advertise themselves, instead they find clueless businessmen and charge them $1000 or so to send their advert to hundreds of thousands of peoples mailboxes

What can be done?

Filter them out or bounce them back. If you receive email via a unix system you may be able to run procmail filters. If not you can use the spam filter capabilities of your mail reader, or one of the many third party filtering tools

Hunt the perpetrators down. Complain to their upstream provider. Repeatedly. Get them shut down. Get the servers, web pages and email addresses of the advertisers yanked. Invoice them for your time, sue them in a small claims court when they don't pay up. Report the illegal schemes to the local police.

Tracing the perpetrators using spamware, or the spamhauses requires some familiarity with email headers - see the tutorials for more information on those


The Spam Sketch

The cast:

Man
Eric Idle
Wife
Graham Chapman
Waitress
Terry Jones

The sketch:

Scene: A cafe. One table is occupied by a group of Vikings with horned helmets on. A man and his wife enter.
Man | You sit here, dear.
Wife | All right.
Man | (to Waitress) Morning!
Waitress | Morning!
Man | Well, what've you got?
Waitress | Well, there's egg and bacon; egg sausage and bacon; egg and spam; egg bacon and spam; egg bacon sausage and spam; spam bacon sausage and spam; spam egg spam spam bacon and spam; spam sausage spam spam bacon spam tomato and spam;
Vikings | (starting to chant) Spam spam spam spam...
Waitress | ...spam spam spam egg and spam; spam spam spam spam spam spam baked beans spam spam spam...
Vikings | (singing) Spam! Lovely spam! Lovely spam!
Waitress | ...or Lobster Thermidor au Crevettes with a mornay sauce served in a Provencale manner with shallots and aubergines garnished with truffle pate, brandy and with a fried egg on top and spam.
Wife | Have you got anything without spam?
Waitress | Well, there's spam egg sausage and spam, that's not got much spam in it.
Wife | I don't want ANY spam!
Man | Why can't she have egg bacon spam and sausage?
Wife | THAT'S got spam in it!
Man | Hasn't got as much spam in it as spam egg sausage and spam, has it?
Vikings | Spam spam spam spam (crescendo through next few lines)
Wife | Could you do the egg bacon spam and sausage without the spam then?
Waitress | Urgghh!
Wife | What do you mean 'Urgghh'? I don't like spam!
Vikings | Lovely spam! Wonderful spam!
Waitress | Shut up!
Vikings | Lovely spam! Wonderful spam!
Waitress | Shut up! (Vikings stop) Bloody Vikings! You can't have egg bacon spam and sausage without the spam.
Wife | (shrieks) I don't like spam!
Man | Sshh, dear, don't cause a fuss. I'll have your spam. I love it. I'm having spam spam spam spam spam spam spam beaked beans spam spam spam and spam!
Vikings | (singing) Spam spam spam spam. Lovely spam! Wonderful spam!
Waitress | Shut up!! Baked beans are off.
Man | Well could I have her spam instead of the baked beans then?
Waitress | You mean spam spam spam spam spam spam... (but it is too late and the Vikings drown her words)
Vikings | (singing elaborately)Spam, spam, spam, spam. Lovely spam! Wonderful spaaam! Lovely spam! Wonderful spam. Spa-a-a-a-a-a-a-am! Spa-a-a-a-a-a-a-am! Spa-a-a-a-a-a-a-am! Spa-a-a-a-a-a-a-am! Lovely spam! (Lovely spam!) Lovely spam! (Lovely spam!) Lovely spaaam! Spam, spam, spam, spaaaaam!


Online Resources

  • Spam fighting at abuse.net - Lots of good stuff hosted by abuse.net, the net abuse clearinghouse. Includes lots of ways of filtering spam of all flavours from your account or an entire site.
  • Mail Abuse Prevention System, LLC
  • S.P.U.T.U.M. - Primarily about usenet spam rather than email spam. Well worth a read, particularly SpuTools®
  • SamSpade.org - the home of Sam Spade. Lots of online tools and yet more documentation and tutorials
  • The Anti-Spam HOWTO - Good, short introduction to the basics of decoding headers and finding the right postmaster to complain to
  • The Coalition Against Unsolicited Commercial Email - Lots of background, history and politics
  • All about spam - Introduction to spam, and a few ways to reduce the amount you get
  • Death to Spam! - Some good advice on retaliating against spam. Be very careful about threatening Denial-of-Service attacks against the host ISP, though. Doing it is illegal in most places, threatening it may be illegal in some.
  • Ed Falks' Spam Tracking Page - the latest news on spam, and a lot of tutorials and links
  • news.admin.net-abuse.email - there's a pointer to a few FAQs at http://samspade.org/d/nanaefaq.html
  • news.admin.net-abuse.usenet
  • The news.admin.net-abuse.* homepage
  • Help! I've been Spammed! What do I do?
  • Mail filtering FAQ
  • Yahoo - spam - Lots more resources at Yahoo!
  • Whiteice, a spam-filtered email service
  • NewsGuy, a spam-filtered usenet service. Tell 'em blighty sent you.
  • Forum for Responsible and Ethical Email
  • GeekTools
  • SueSpammers.org
  • SpamNews
  • JunkBusters - defeating telemarketers and other vermin


Sam Spade Menus

Menus

  • File
  • Tools
  • View
  • Window
  • Tools
  • Help


Sam Spade File Menu

File Menu

  • Save As...
  • Clear log
  • Exit


Save As...

This will save the currently selected window to a file


Clear log

This will delete the contents of the log window


Sam Spade Edit Menu

Edit Menu

  • Copy
  • Paste
  • Smart Paste
  • Options...


Copy

Selecting Edit → Copy or the copy button - [image] - copys the contents of the active window to the windows clipboard


Paste

If you copy an address to the windows clipboard and hit the paste button - [image] - it will be pasted into the address box

If you have a suspicious looking set of email headers you can copy them to the clipboard from your mail reader and paste them into Sam Spade using Edit → Paste or the paste button - [image]. They'll appear in a new window, with any addresses highlighted as active text.

If you have Smart Paste enabled Sam Spade will run a rudimentary header analysis. It's not perfect, but can sometimes find suspicious parts of the header

You'll need to paste full headers for header analysis to be useful


Smart Paste

If this option is selected and you paste email headers from the clipboard Sam Spade will look at the headers looking for suspicious lines


Sam Spade View Menu

View Menu

  • Address Bar - shows or hides the address bar
  • Toolbar - shows or hides the toolbar
  • Options Bar - shows or hides the options bar
  • Status Bar - shows or hides the status bar
  • Captions - shows or hides toolbar captions
  • Scripts - shows or hides the script console


Sam Spade Window Menu

Window Menu

  • Cascade
  • Tile
  • Arrange Icons

None of these three are particularly useful, but they're traditional in an MDI application

They're followed by a list of the child windows


Sam Spade Tools Menu

Tools Menu

  • Zone Transfer... - ask a DNS server for all it knows about a domain
  • SMTP Relay Check... - check whether a mail server allows third party relaying
  • Scan Addresses... - scan a range of IP addresses looking for open ports
  • Crawl website... - search a website, looking for email addresses, offsite links etc.; download a website (Sam Spade isn't the best tool to use for mirroring a website - look at wget instead).
  • Browse web... - browse the web in a raw http format
  • Check cancels... - search your newsserver for cancel messages
  • Fast Traceroute... - find the route packets take between you and a remote system
  • Slow Traceroute... - find the route packets take between you and a remote system (slower, but occasionally more reliable)
  • S-Lang command... - issue a scripting command; useful for debugging scripts.
  • Decode URL... - decipher an obfuscated URL
  • Parse email headers... - read email headers and make a guess about the origin of the email


Relay checking

SMTP Relay Checking

A common trick used by senders of UCE is to send their spam via a third party relay

Why? Two main reasons

  1. It allows them to send much, much more email than they could without it. They can send one email addressed to a few hundred email addresses to the relay. The relay will then expand that to a few hundred separate emails, one sent to each recipient. This allows them to send a hundred thousand emails an hour over a cheap dialup account rather than the couple of thousand they could send without stealing service from the relay
  2. It obfuscsates the headers, making it more difficult to track the original sender. In the case of badly configured relays which don't record the IP address of the sender it makes it impossible to find the originator without the co-operation of the system administrator of the relay

The only sure way to tell whether an SMTP server is insecure (allowing anyone to relay email) is to try and relay an email through it back to yourself

Some system administrators will get upset if you do this. While any reasonable sysadmin shouldn't object if you check that his system is insecure, then politely tell him so, there are a fair number of insecure sysadmins who'll go off the deep end and threaten law suits if you probe their servers. There are also servers which are secure, but which log relay attempts. Either way, don't probe servers on a whim, and send email to their admins afterwards explaining why you were probing their server

Before you can run a relay check you need to do two things

  • Select Edit→Options...→Configuration and enter your email address
  • Select Edit→Options...→Advanced and select Enable active probing

Then select Tools→SMTP Relay Check..., enter the server you want to probe and hit OK

A new window will open, showing the transaction. Even if the server seems to accept your relayed email it may throw it away without telling you. You don't know that it allows relaying until the relayed email arrives in your inbox


DNS Zone Transfer

A zone transfer asks a nameserver for all the information it knows about a domain. This is only really useful if the nameserver is authorative for that domain (to find a nameserver authorative for a domain run dig on that domain and look for NS entries)

A zone transfer puts a lot of load on a nameserver, and is considered impolite, so some nameservers won't allow zone transfers

Before you run a zone transfer you'll need to select Edit → Options... → Advanced and select Enable zone transfers

Then select Tools → Zone Transfer... Enter the domain you're interested in and the nameserver you want to interrogate

This returns a lot of data, so you may want to save the results to a file rather than displaying them


Active port scanning

It can be interesting to see which internet services a range of machines provide (it's often possible to find interesting information about a system by accessing those services using telnet)

Before you can scan for open ports you must select Edit → Options... → Advanced then select Enable active probing

Select Tools → Scan Addresses...

Enter the range of addresses you want to probe, and select the services you want to look for

  • Reverse DNS - this isn't a service, instead selecting this box attempts to lookup the hostname of each IP address scanned
  • Mail - port 25, SMTP mail
  • Usenet - port 119, usenet news
  • Web - port 80, http web service
  • Telnet - port 23, remote login
  • Nameserver - port 42, nameserver

If you want to scan a wider selection of ports hit the Advanced>> button and select ports in the scrollable list

Note scanning a lot of ports seems to put a lot of stress on the windows sockets library. Strange bugs and crashes happen with some versions of winsock


Browse web

Hitting [image] or selecting Tools → Browse web... starts a very simple, secure web browser

Instead of rendering the HTML it receives it displays the raw http traffic - both the http headers and the raw HTML body

This can be useful for looking at deceitful or malign websites, or for debugging cgi scripts

Usually a new window will be opened for each webpage, but if the single window button - [image] - is selected the window will be reused


Crawl web

Tools → Crawl website... will download a website to local disc, search a website for useful info or both

It will crawl all webpages it can find below the URL you give it

If there are pages below this URL that may not be reachable from the top level you can add extra seed URLs

As well as the predefined patterns you can also search for arbitrary regular expressions

It's intentionally a dumb, impolite webcrawler, as it's designed for searching malign websites. It ignores the robots.txt file designed to tell webcrawlers where they're allowed to look. It ignores the META tags that do the same thing.

If the website includes self-referential cgi scripts designed to trap webcrawlers in an endless recursion they'll trap this one


Checking for usenet cancels

When usenet was designed it was reasonable to expect none of the users would attack the infrastructure. Cancels were supposed to be used if someone posted something wrong and wanted to cancel their own post

The cancel feature is now used to automatically cancel the vast majority of usenet spam. Because of this many usenet spammers want to persuade newsserver administrators to disable cancels on their servers (so more people will read their spam)

One spammer, Mike Enlow, with the connivance of win.net, has released a program that allows people to issue massive cancels for messages containing any particular word. His hopes of persuading news admins to turn off cancels look unlikely to succeed, thanks to the efforts of some members of S.P.U.T.U.M.

Before checking for cancels you need to enter your newsserver in Edit → Options → News

Select Tools → Check cancels...

To check a single group, e.g. alt.stagecraft, enter *alt.stagecraft*

Each check will take some time, and will take the same amount of time for one group as for multiple groups so it makes sense to check multiple groups at once. You can specify multiple groups using wildmat format

This function will only work if your newserver accepts the XPAT command


Sam Spade Help Menu

Help Menu

  • Tip of the Day... - show the Tip of the Day window usually shown as Sam Spade starts
  • Home page... - view Sam Spade homepage
  • About... - show release information, system information and check whether a new release is available


Toolbar

Close current window and append the contents to the log
Append the current window to the log
Copy the current window contents to the clipboard
If there is a single line of text on the clipboard, paste it to the address box. If there's more than one line, paste it to a new window, then run a header analysis on it if Smart Paste is enabled
Ping the address
DNS lookup - find the hostname and IP address of the address
whois - find who owns the domain name
IP block - find who owns the address
dig - perform a DNS query for the address
traceroute - find the route packets take between you and the remote system
finger the address
SMTP verify - ask the mailserver to verify the address
Browse the web
Keep a dialup connection alive
Check several blackhole lists
Query the time on a remote system
Find the email address of an abuse desk
Make the text in the current window larger
Make the text in the current window smaller


Ping

Ping - [image] - sends a series of packets to the current address to see if it's alive, and how long it takes packets to make the round trip

The number of packets sent can be adjusted on the options bar -


DNS tool

Perform forward and reverse DNS queries for the current address (this will usually give you the IP address of a hostname and the hostname of an IP address)


whois

(Adapted from a tutorial by Ed Falk - see his original document for the full details)

When you hit the whois button - [image] - Sam Spade will contact one of the network registries to find out contact information for the current domain or IP address

Magic?

whois can be used in two different modes. You select between these using the whois server box

Simple whois

Usually the Magic server is selected, and Sam Spade will automagically choose the right whois server to contact.

In this mode Sam Spade will try to perform a sensible whois lookup for any form of address entered in the address box

The whois result will appear in a new window.

Advanced whois

You can select a specific server in the whois server box to send whois queries to. When you do this you have access to a much wider selection of arguments (which can be entered in the address box):

(These are the arguments accepted by whois.internic.net. Use the help command to find the arguments accepted by other servers)

help
Get information on various arguments and their meanings.
sitename
Where sitename is the domain name of the site for which you want information. Only give the domain name (e.g. online18.com), and not the full host name (e.g. www.online18.com). This will give you all the information the internic has about the given site. The whois server will attempt to match this name against all types of records: name, nicknames, hostname, net address, etc.) If there are multiple matches, whois will list them, one per line. If there is a single match, whois will give all the information about it.
pattern.
Where pattern. is the partial pattern you wish to match (note the terminating dot). Whois will return all patterns that begin with this pattern. Example: online18.).
IP Address
Where IP Address is the full IP address in dotted-decimal notation of a host, (e.g.206.138.239.10). Whois will return the hostname for this address if it is found in the database. This can often be used to determine a site's network server.
subnet (this is the same as the IP Block tool)
Where subnet is the IP address in dotted-decimal notation (e.g 206.138.239.0 or 206.138.239) Whois will return the site or sites listed under this subnet. If there is no match, make the search more broad (e.g. 206.138.0.0 or 206.138); this can often be used to determine a site's upstream provider.
subnet.
(Note the terminating dot). Find all addresses matching this pattern, e.g. 206.138.239. returns all hosts belonging to Online18.
HA handle
!handle
Where !handle is a handle returned when there are multiple matches, (e.g. !ONLINE16-DOM or !CF624 This will return the single indicated record.
NAme name
.name
Search only the Name field. Example: .Chandler, Andrew
M mailbox
mailbox@addr
Search only the Mailbox field. Example: admin@xxxyes.com
DOmain pattern
Search only domain records, e.g. "COM", "DOM MIT" or "DOM MIT.EDU".
GAteway pattern
Search only gateway records.
HOst pattern
Search only host records, e.g. HO WWW1032-HST
SErver pattern
Search only server records. This can be very useful for finding out which sites share the same server. For example, the search: server www1032-hst. reveals that the porn spammer Online18 is the same as -- or associated with -- the Psychic Spammer
ASm pattern
Search only autonomous system numbers records.
NEtwork pattern
Search only network records, e.g. NE WWW1032-HST
O pattern
Search only organization records.
Full search
Give full records, even if multiple matches.
SUMmary search
Give summary records, even if single match.


IP Block lookup

Selecting the IP Block button - [image] - will attempt to lookup the owner of the current address

The results will appear in a new window in the same way as whois

If the whois server selected is Magic the IP block lookup will try multiple servers if needed to find the owner of the block. If a specific server is selected only that server will be queried


Advanced DNS

Advanced DNS (dig)

This tool requests all the DNS records for a host or domain

A | a host address
NS | an authoritative name server
MD | a mail destination (Obsolete - use MX)
MF | a mail forwarder (Obsolete - use MX)
CNAME | the canonical name for an alias
SOA | marks the start of a zone of authority
MB | a mailbox domain name (EXPERIMENTAL)
MG | a mail group member (EXPERIMENTAL)
MR | a mail rename domain name (EXPERIMENTAL)
NULL | a null RR (EXPERIMENTAL)
WKS | a well known service description
PTR | a domain name pointer
HINFO | host information
MINFO | mailbox or mail list information
MX | mail exchange
TXT | text strings
RP | Responsible person
AFSDB | AFS database (RFC1183)
X25 | X25 (RFC1183)
ISDN | ISDN (RFC1183)
RT | Route through (RFC1183)
NSAP | NSAP (RFC1637,1348)
NSAP_PTR | NSAP-PTR
SIG | RFC2065
KEY | RFC2065
PX | Preference (RFC1664)
GPOS | Geographical position, also known as the ICBM record (RFC1712)
AAAA | IPv6 Address (RFC 1886)
LOC | Location, also known as ICBM record (RFC1876)
NXT | RFC2065
EID | draft-ietf-nimrod-dns-xx.txt
NIMLOC | draft-ietf-nimrod-dns-xx.txt
SRV | Services (RFC 2052)
ATMA | ?
NAPTR | (RFC2168)
TSIG | draft-ietf-dnsind-tsig-xx.txt
UINFO | Non standard
UID | Non standard
GID | Non standard
UNSPEC | Non standard
IXFR | RFC1995
AXFR | A request for a transfer of an entire zone
MAILB | A request for mailbox-related records (MB, MG or MR)
MAILA | A request for mail agent RRs (Obsolete - see MX)
ALL | A request for all records


Traceroute

Traceroute - [image] - finds the route packets take between you and the selected address

Traceroute isn't foolproof. Some networks block ICMP packets at their boundaries, making it impossible to run traceroute into their systems.

Traceroute will send three packets for each hop of the route. Each packet that doesn't return is shown as an asterisk. If none of the three packets return from a stage it will be shown as * * * failed

If a single stage fails, but later stages return data it means the one stage doesn't respond to traceroute packets

If traceroute returns data up to a stage, but all stages after that fail it usually means that the stage after the final one is a firewall blocking ICMP packets. This is... suspicious.

A few older routers are faulty in the way they handle traceroute packets. They will set the lifetime of the error packets they return to the same lifetime as the packet they receive. This is pretty rare (I've never seen it) but the symptom is that stages 1..n-1 will return traceroute packets, stages n..2*n-1 will fail and stage 2*n will return the packet that should have appeared in stage n

Another description of traceroute


Finger

Finger - [image] - asks a server about one of it's users. Many machines have finger disabled, but if it's available it can give a lot of interesting information


Finger

SMTP Verify

Verify - [image] - asks a mail server about one of it's users. Many machines have verify disabled, but if it's available it can give a lot of interesting information


Keepalive

If you select the keepalive button - [image] - Sam Spade will send a web request to your providers web server once a minute to keep your dialup connection from timing out

Before you use this you'll need to select Edit→Options...→Configuration and enter the address of your providers web page


Blackhole Lists

The Mail Abuse Prevention System maintains three blackhole lists which can be used to block email which is likely to be unwanted.

This tool checks whether a host is in any of those lists

Realtime Blackhole List

The RBL is a manually maintained list containing those hosts which are major sources of unwanted email or which provide services to those who send unsolicited bulk email.

Dynamic Dial-Up List

The DUL is a list of many dynamically assigned dialup addresses. These systems shouldn't normally be a direct source of legitimate email.

Relay Spam Stopper

The RSS is a list of systems which will relay email for anyone and which have at some point been abused to relay spam


Abuse.net lookup

Abuse.net maintains a list of the abuse desk email addresses for a number of domains. This tool queries their database.


Options Bar

The number of packets to send on each ping. If this is set to 0 ping will repeat forever
Choose a whois server, or select automagic choice of server
Choose a DNS nameserver
Configure the web browser to use one window or multiple windows


Status bar

The status bar has three panes

The first displays flyby help as you move the cursor over controls

The second and third display the number of active network transactions


Troubleshooting

Configuration

Make sure you've configured your email address and nameserver, or many functions will fail with strange error messages

Greyed functions on the Tools menu

Several functions on the tools menu need to be enabled before you can use them. Select Edit → Options... → Advanced and enable the functions you want to use

Problems analysing headers

Netscape

Netscape Navigator 3 and 4 both make it difficult to copy headers to the clipboard. Sam Spade attempts to undo the corruption in headers copied from Navigator, but sometimes fails. The right way to copy headers from Navigator is to select the message, select View → Document Source, select the header, then hit Ctrl-C to copy the header to the clipboard

(Navigator is a bad mailreader and a worse newsreader. You're much better off if you use Navigator as a web browser, and use a real mailreader and newsreader. Internet Explorer is no better.)

Online Help

HTML Help seems to work only with Internet Explorer 4. Anything else and you should use WinHelp instead. Select Edit → Options... → Help. You're not missing any content - the WinHelp is generated automatically from the HTMLHelp

Anything else

Check the support page for known bugs and bug reporting information


Technical support

Bug reports, enhancement requests

Check the Sam Spade homepage for new versions, known bugs and info about the program itself

Drop me some email at spade@blighty.com

How to use the tools

What? You mean the online help isn't good enough for you? Most of the tools Sam Spade provides are nearly identical to their unix equivalents, so any tutorials you find referring to the unix tools will be useful

Check some of the online resources for some pointers

Reading headers, tracing spam etc

Take a look at the online resources for a good start

Subscribe to the newsgroups news.admin.net-abuse.email or news.admin.net-abuse.usenet

These groups are very high traffic, and have a large number of saboteurs trolling, forging posts and posting complete lies. Lurk for a week or so before posting to get some idea of who the trolls are and whose advice you should trust

See the Tip of the day notes on kooks and trolls, and take a look at news.admin.net-abuse.* homepage too


Sam Spade Credits

Credits

Design, implementation, documentation

  • Steve Atkins

Inspiration

  • The regulars of news.admin.net-abuse.email
  • The SPAM-L mailing list
  • S.P.U.T.U.M.
  • The alt.gothic Special Forces

Alpha and Beta testers

Far too many to remember - here're a few:

  • JOWazzoo
  • Jim Rusling
  • Dave Stoddard
  • Captain Blade
  • Ruben Bunyee
  • Joe Shields
  • Graycat
  • John Lambert
  • Jeff Duggin
  • Reinhold Gerharz
  • Marek Jedlinski
  • Lowell Anderson
  • Jeff Weiss
  • Joel Levin
  • Todd & Lori
  • Bruce McGlashan
  • Chingteng Hsiao
  • Paul Lee
  • Mike Wendell
  • Kurt Weirich
  • RTG Parners
  • Mark (from comports)
  • Ron Kulik
  • Karl Carapellotti
  • Don McCallum
  • Keesjan Steenbergen
  • Scott Taylor
  • Jason Soled
  • Doktor DynaSoar Iridium
  • Ellen Holmes
  • TheCharlie
  • rrevved
  • Shmuel Metz
  • Darrell Larose
  • Jerry Brenner
  • Debra Walker

And everyone else

  • Ed Falk
  • Mr Sam
  • Patricia Shaffer
  • Tim Skirvin
  • Countless people who gave me feedback about bugs in the online version - all that development was folded into Sam Spade
  • Chris Siebenman
  • Jordan Russell, the author of Inno Setup


Default nameserver

You need to enter the nameserver your ISP told you to use here. If you don't have it noted you can find it:

  1. Open the Windows Control Panel (Start->Settings->Control Panel)
  2. Open the Network dialog and select the Configuration tab
  3. Select TCP/IP and click on Properties
  4. Select DNS Configuration
  5. The address you need is the top address in the DNS Server Search Order box

(That's for Windows 95 - it's similar for NT)

Only enter one address in the default nameserver box


News Server

This is the newsserver that Sam Spade will use when checking a newsgroup for cancels

If you can't remember your newsserver you can find it:

Forte Agent
Options → User and System Profile → System
Anawave Gravity 1.1
View → Options... → Setup
Anawave Gravity 2.0
?
Netscape Navigator 3
Options → Mail and News Preferences... → Servers
Netscape Communicator
Edit → Preferences... → Mail & Groups → Groups Server
Internet Explorer 3
?
Microsoft Outlook Express
Tools → Accounts...


Tracking the source of email spam

Dissecting an email header

by Ed Falk

Spammers often forge the headers of their email in an attempt to avoid losing their accounts and to evade email filters. These notes may help you track the source of spam.

The most important thing is to have a mail reader that can show you the full headers of an email in question. The important lines are as follows:

From:
Who the message is from. This is the easiest to forge, and thus the least reliable.

From
As distinct from the "From:" line. This line is not actually part of the email header, but mail transfer software often inserts it when the mail is received. Many Unix mailers use this line to seperate messages in a mail folder. This line will always be the first line in the headers.

This line can also be forged, but not always.

Reply-To:
The address to which replies should be sent. Often absent from the message, and very easily forgeable. However, it often provides a clue. For example, forged spam often has a legitimate Reply-To: field so that the spammer can receive mail orders.

Return-Path:
The email address for return mail. Same as Reply-To:

Sender:
The account that sent the message. Mail software is supposed to insert this line if the user modifies the From: line. Most Mail software is broken in this respect, so this line is rarely present. Some mailers provide an X-Sender: line.

Message-ID:
A unique string assigned by the mail system when the message is first created. This is also forgeable in most cases, but requires a little more specialized knowledge than forging the From: line. Also, the Message-ID: often identifies the system from which the sender is logged in, rather than the actual system where the message originated.

The format of a Message-ID: field is <unique string>@<sitename>

Each kind of mail software has its own style of unique string. Sloppy forgeries often get it wrong, thus a forgery can be confirmed by comparing the message id with some legitimate messages from that same site.

Received:
These are the most reliable lines in the header. They form a list of all sites through which the message traveled in order to reach you. They are completely unforgeable after the point where it was injected. Up to that point, they may be forgeries.

Each mail system has their own style of Received: line. A Received: line typically identifies the machine that received the mail and the machine that the mail was received from. I.e.:

    Received: from foo.com by bar.com id AA15057; Fri, 25 Jul 97 09:39:02

The "foo.com" part is the name that the sending machine used to identify itself. This may be forged in the case of spam. The id is for logging purposes and may help system administrators track the spam if you can get them to cooperate with you.

Many mailers will add extra information. For example:

    Received: from foo.com ([129.2.3.4])  ...

In this case, the mailer has inserted the IP address of the sending system. If the machine name does not match the IP address, then you have likely identified the point where the mail was injected. In other words, the machine whose address is 129.2.3.4 lied when it identified itself as foo.com. Any Received: lines that follow are likely to be forgeries.

If the IP address does not make sense (for instance, no component may be greater than 255), then this entire Received: line is a fake. Contact a system admin for more advice in determining if an IP address is bogus. If the entire Received: line is fake, then the injection point is somewhere above in the headers.

Sometimes you will see

    Received: from foo.com (x.y.alterdial.uu.net [129.2.3.4])  ...

In this case, the mailer has inserted both the IP address and the real name of the sending system. This will help you identify forgeries and eliminate the need to look up the IP address by hand.

Comment:
Some mailers may add additional information to the headers, such as "Authenticated sender is doe@foo.com". Forged Comment: lines can be easily added to outgoing mail, so this line is likely to be fake, but not always.

Other mailers may insert their own authentication information in the headers.

Here is an example of a forgery:

  From webpromo@denmark.it.earthlink.net  Tue Jul  8 13:05:02 1997
  Return-Path: <webpromo@denmark.it.earthlink.net>
  From: webpromo@denmark.it.earthlink.net
  Received: from denmark.it.earthlink.net (denmark-c.it.earthlink.net
    [204.119.177.22]) by best.com (SMI-8.6/mail.byaddr) with ESMTP id
    NAA21506 for <falk@falconer.vip.best.com>;
    Tue, 8 Jul 1997 13:05:16 -0700
  Received: from mail.earthlink.net (1Cust98.Max16.Detroit.MI.MS.UU.NET
      [153.34.218.226]) by denmark.it.earthlink.net (8.8.5/8.8.5)
     with SMTP id NAA12436; Tue, 8 Jul 1997 13:00:46 -0700 (PDT)
  Received: from adultpromo@earthlink.net by adultpromo@earthlink.net
    (8.8.5/8.6.5) with SMTP id GAA05239 for <adultpromo@earthlink.net>;
    Tue, 08 Jul 1997 15:48:51 -0600 (EST)
  To: adultpromo@earthlink.net
  Message-ID: <199702170025.GAA08056@no-where.net>
  Date: Tue, 08 Jul 97 15:48:51 EST
  Subject: Hot News !
  Reply-To: adultpromo@earthlink.net
  X-PMFLAGS: 12345678 9
  X-UIDL: 1234567890x00xyz1x128xyz426x9x9x
  Comments: Authenticated sender is <adultpromo@earthlink.net>
  Content-Length: 672
  X-Lines: 26
  Status: RO

Obviously, the To: line is a forgery; the actual recipients list was hidden, probably with a blind carbon-copy (Bcc: header)

The "From", "Return-Path:" and "From:" all identify the same email address, but that may be a forgery. You can try mailing to the given address and see if your complaint bounces.

The "To:", "Reply-To:" and "Authenticated sender" lines all identify a different account. Again, these may all be forgeries.

The Message-ID: line is an obvious fake.

The first Recieved: line shows the mail arriving at my service provider from Earthlink. I trust my service provider, so this line is almost certainly valid.

The second Received: line shows this inconsistency:

  ... from mail.earthlink.net (1Cust98.Max16.Detroit.MI.MS.UU.NET
      [153.34.218.226])

In other words, the machine that delivered the mail to denmark.it.earthlink.net identified itself as mail.earthlink.net but was actually named 1Cust98.Max16.Detroit.MI.MS.UU.NET. This is very likely a lie. However, Earthlink rents POPs from Uunet, so this might be an Earthlink customer dialing in from Uunet.

The third Received: line is completely bogus. If the mail came from a dial-in customer at Uunet, there wouldn't be any more Recieved: lines. If the mail was being relayed from Uunet, this Received: line would indicate Uunet, not Earthlink. Finally, this Received: line contains email addresses, not machine names.

Clearly, this email was forged to make it look like it came from Earthlink but was actually injected from Uunet. Whether this was by an Earthlink customer or some other Uunet customer is impossible to tell without cooperation from Earthlink sysadmins.

Here is another forgery:

  Received: from cola.bekkoame.or.jp (cola.bekkoame.or.jp [202.231.192.40])
    by srv.net (8.8.5/8.8.5) with ESMTP id BAA00705
    for <got@srv.net>; Wed, 30 Jul 1997 01:15:27 -0600 (MDT)
  From: beautifulgirls585@aol.com
  Received: from cola.bekkoame.or.jp
    (ip21.san-luis-obispo.ca.pub-ip.psi.net [38.12.123.21]) by
    cola.bekkoame.or.jp (8.8.5+2.7W/3.5W) with SMTP id OAA11439;
    Wed, 30 Jul 1997 14:35:50 +0900 (JST)
  Received: from mailhost.aol.com(alt1.aol.com(244.218.07.32)) by
    aol.com (8.8.5/8.6.5) with SMTP id GAA00075 for <"">;
    Tue, 29 Jul 1997 22:19:42 -0600 (EST)
  Date: Tue, 29 Jul 97 22:19:42 EST
  Subject: You can have what you want...
  Message-ID: <574857638458.HWF39862@aol.com>
  Reply-To: beautifulgirls585@aol.com
  X-PMFLAGS: 56354433 0
  Comments: Authenticated sender is <aol.com>
  X-UIDL: vjg79u26gfkjjrty38jf983j309jfyrw

Here, the second Received: line indicates that "cola.bekkoame.or.jp" received the mail from a machine which identified itself as "cola.bekkoame.or.jp", but was in fact "ip21.san-luis-obispo.ca.pub-ip.psi.net". This mail was probably forged from a Psi.net dial-in account.

As a final proof, the IP address mentioned in the third Received: line cannot be matched via whois or traceroute. It certainly doesn't match AOL, indicating that this line is bogus.


Notes on whois

(Adapted from a tutorial by Ed Falk - see his original document for the full details)

The argument to whois may be one of the following:

help
Get information on various arguments and their meanings.
sitename
Where sitename is the domain name of the site for which you want information. Only give the domain name (e.g. whois online18.com), and not the full host name (e.g. www.online18.com). This will give you all the information the internic has about the given site. The whois server will attempt to match this name against all types of records: name, nicknames, hostname, net address, etc.) If there are multiple matches, whois will list them, one per line. If there is a single match, whois will give all the information about it.
pattern.
Where pattern. is the partial pattern you wish to match (note the terminating dot). Whois will return all patterns that begin with this pattern. Example: whois online18.).
IP Address
Where IP Address is the full IP address in dotted-decimal notation of a host, (e.g.whois 206.138.239.10). Whois will return the hostname for this address if it is found in the database. This can often be used to determine a site's network server.
subnet
Where subnet is the IP address in dotted-decimal notation (e.g whois 206.138.239.0 or 206.138.239) Whois will return the site or sites listed under this subnet. If there is no match, make the search more broad (e.g. whois 206.138.0.0 or 206.138); this can often be used to determine a site's upstream provider.
subnet.
(Note the terminating dot). Find all addresses matching this pattern, e.g.whois 206.138.239. returns all hosts belonging to Online18.
HA handle
!handle
Where !handle is a handle returned when there are multiple matches, (e.g. whois !ONLINE16-DOM or whois !CF624 This will return the single indicated record.
NAme name
.name
Search only the Name field. Example: whois .Chandler, Andrew
M mailbox
mailbox@addr
Search only the Mailbox field. Example: whois admin@xxxyes.com
DOmain pattern
Search only domain records, e.g. "COM", "DOM MIT" or "DOM MIT.EDU".
GAteway pattern
Search only gateway records.
HOst pattern
Search only host records, e.g. whois HO WWW1032-HST
SErver pattern
Search only server records. This can be very useful for finding out which sites share the same server. For example, the search: whois server www1032-hst. reveals that the porn spammer Online18 is the same as -- or associated with -- the Psychic Spammer
ASm pattern
Search only autonomous system numbers records.
NEtwork pattern
Search only network records, e.g. whois NE WWW1032-HST
O pattern
Search only organization records.
Full search
Give full records, even if multiple matches.
SUMmary search
Give summary records, even if single match.


Decode URL

This tool will decode many obfuscated URLs.


Email header parser

This tool analyses the headers of an email you paste into it, explaining each Received line. It is a fairly crude tool, and its suggestions are often wrong (though it tries to be conservative in its statements). Don't rely blindly on the results.

It also opens a mail send window, including a quoted copy of the email you pasted in.

When a mail send window is open you can add email addresses to it by right-clicking on an email address or hostname in another window.

This can be configured using the startup script


USA Area Codes

201 | New Jersey
202 | District of Columbia
203 | Connecticut
204 | Manitoba
205 | Alabama
206 | Washington
207 | Maine
208 | Idaho
209 | California
210 | Texas
212 | New York
213 | California
214 | Texas
215 | Pennsylvania
216 | Ohio
217 | Illinois
218 | Minnesota
219 | Indiana
224 | Illinois
225 | Louisiana
228 | Mississippi
231 | Michigan
240 | Maryland
242 | Bahamas
246 | Barbados
248 | Michigan
250 | British Columbia
252 | North Carolina
253 | Washington
254 | Texas
256 | Alabama
262 | Wisconsin
264 | Anguilla
267 | Pennsylvania
268 | Antigua/Barbuda
270 | Kentucky
281 | Texas
284 | British Virgin Islands
301 | Maryland
302 | Delaware
303 | Colorado
304 | West Virginia
305 | Florida
306 | Saskatchewan
307 | Wyoming
308 | Nebraska
309 | Illinois
310 | California
311 | Non-Emergency Access
312 | Illinois
313 | Michigan
314 | Missouri
315 | New York
316 | Kansas
317 | Indiana
318 | Louisiana
319 | Iowa
320 | Minnesota
321 | Florida
323 | California
330 | Ohio
331 | Illinois
334 | Alabama
336 | North Carolina
337 | Louisiana
340 | US Virgin Islands
341 | California
345 | Cayman Islands
347 | New York
352 | Florida
360 | Washington
361 | Texas
401 | Rhode Island
402 | Nebraska
403 | Alberta
404 | Georgia
405 | Oklahoma
406 | Montana
407 | Florida
408 | California
409 | Texas
410 | Maryland
411 | Local Directory Assistance
412 | Pennsylvania
413 | Massachusetts
414 | Wisconsin
415 | California
416 | Ontario
417 | Missouri
418 | Quebec
419 | Ohio
423 | Tennessee
424 | California
425 | Washington
435 | Utah
440 | Ohio
441 | Bermuda
442 | California
443 | Maryland
450 | Quebec
456 | Inbound International
464 | Illinois
469 | Texas
473 | Grenada
480 | Arizona
484 | Pennsylvania
500 | Personal Communication Services
501 | Arkansas
502 | Kentucky
503 | Oregon
504 | Louisiana
505 | New Mexico
506 | New Brunswick
507 | Minnesota
508 | Massachusetts
509 | Washington
510 | California
512 | Texas
513 | Ohio
514 | Quebec
515 | Iowa
516 | New York
517 | Michigan
518 | New York
519 | Ontario
520 | Arizona
530 | California
540 | Virginia
541 | Oregon
555 | Not Available
559 | California
561 | Florida
562 | California
570 | Pennsylvania
571 | Virginia
573 | Missouri
580 | Oklahoma
586 | Michigan
600 | Canada (Services)
601 | Mississippi
602 | Arizona
603 | New Hampshire
604 | British Columbia
605 | South Dakota
606 | Kentucky
607 | New York
608 | Wisconsin
609 | New Jersey
610 | Pennsylvania
611 | Repair Service
612 | Minnesota
613 | Ontario
614 | Ohio
615 | Tennessee
616 | Michigan
617 | Massachusetts
618 | Illinois
619 | California
623 | Arizona
626 | California
628 | California
630 | Illinois
631 | New York
636 | Missouri
646 | New York
647 | Ontario
649 | Turks & Caicos Islands
650 | California
651 | Minnesota
657 | California
660 | Missouri
661 | California
662 | Mississippi
664 | Montserrat
669 | California
670 | CNMI
671 | Guam
678 | Georgia
700 | IC Services
701 | North Dakota
702 | Nevada
703 | Virginia
704 | North Carolina
705 | Ontario
706 | Georgia
707 | California
708 | Illinois
709 | Newfoundland
710 | U.S. Government
711 | TRS Access
712 | Iowa
713 | Texas
714 | California
715 | Wisconsin
716 | New York
717 | Pennsylvania
718 | New York
719 | Colorado
720 | Colorado
724 | Pennsylvania
727 | Florida
732 | New Jersey
734 | Michigan
740 | Ohio
752 | California
757 | Virginia
758 | St. Lucia
760 | California
763 | Minnesota
764 | California
765 | Indiana
767 | Dominica
770 | Georgia
773 | Illinois
775 | Nevada
780 | Alberta
781 | Massachusetts
784 | St. Vincent & Grenadines
785 | Kansas
786 | Florida
787 | Puerto Rico
800 | 800 Service
801 | Utah
802 | Vermont
803 | South Carolina
804 | Virginia
805 | California
806 | Texas
807 | Ontario
808 | Hawaii
809 | Dominican Republic
810 | Michigan
811 | Business Office
812 | Indiana
813 | Florida
814 | Pennsylvania
815 | Illinois
816 | Missouri
817 | Texas
818 | California
819 | Quebec
828 | North Carolina
830 | Texas
831 | California
832 | Texas
843 | South Carolina
847 | Illinois
850 | Florida
856 | New Jersey
858 | California
859 | Kentucky
860 | Connecticut
863 | Florida
864 | South Carolina
865 | Tennessee
867 | Yukon & NW Territories
868 | Trinidad and Tobago
869 | St. Kitts & Nevis
870 | Arkansas
872 | Illinois
876 | Jamaica
877 | 888 Service Expansion
880 | PAID-800 Service
881 | PAID-888 Service
882 | PAID-877 Service
888 | 800 Service Expansion
900 | 900 Service
901 | Tennessee
902 | Nova Scotia
903 | Texas
904 | Florida
905 | Ontario
906 | Michigan
907 | Alaska
908 | New Jersey
909 | California
910 | North Carolina
911 | Emergency
912 | Georgia
913 | Kansas
914 | New York
915 | Texas
916 | California
917 | New York
918 | Oklahoma
919 | North Carolina
920 | Wisconsin
925 | California
931 | Tennessee
935 | California
937 | Ohio
940 | Texas
941 | Florida
949 | California
950 | Not Available
951 | California
952 | Minnesota
954 | Florida
956 | Texas
970 | Colorado
971 | Oregon
972 | Texas
973 | New Jersey
978 | Massachusetts


International Dialing Codes

Afghanistan | 93
Albania | 355
Algeria | 213
Andorra | 376
Angola | 244
Anguilla | 1 809
Antarctic Aus Territory | 672
Antigua and Barbuda | 1 809
Antilles | 599
Argentina | 54
Armenia | 374
Aruba | 297
Ascension Island | 247
Australia | 61
Austria | 43
Azerbaijan | 994
Azores | 351
Bahamas | 1809
Bahrain | 973
Bangladesh | 890
Barbados | 809
Belarus | 375
Belgium | 32
Belize | 501
Belize | 229
Bermuda | 1 809
Bhutan | 975
Bolivia | 591
Bosnia Hercegovina | 387
Botswana | 267
Brazil | 55
Brunei Darussalam | 673
Bulgaria | 359
Bukina Faso | 226
Barundi | 257
Cambodia | 855
Cameroon | 237
Canada | 1
Cape Verde Islands | 238
Cayman Islands | 1 809
Central African Republic | 236
Chad | 235
Chile | 56
China | 86
Christmas Island | 672
Cocos Island | 672
Columbia | 57
Comoros | 269
Congo | 242
Cook Islands | 682
Costa Rica | 506
Cote d'Ivorie | 225
Croatia | 385
Cuba | 53
Cyprus | 357
Czech Republic | 42
Denmark | 45
Djibouti | 253
Dominica | 1 809
Dominican Rebublic | 1 809
Ecuador | 593
Egypt | 20
El Salvador | 503
Equatorial Guinea | 240
Eritrea | 291
Estonia | 372
Ethiopia | 251
Falkland Islands | 500
Faroe Islands | 298
Fiji | 679
Finland | 358
France | 33
French Guiana | 594
French Polynesia | 689
Gabon | 241
Gambia | 220
Georgia | 7 & 995
Germany | 49
Ghana | 233
Gibraltar | 350
Greece | 30
Greenland | 299
Grenada | 1 809
Guadeloupe | 590
Guam | 671
Guatemala | 502
Guinea | 224
Guinea - Bissau | 245
Guyana | 592
Haiti | 509
Honduras | 504
Hong Kong | 852
Hungary | 36
Iceland | 354
India | 91
Indonesia | 62
Iran | 98
Iraq | 964
Ireland Republic of | 353
Israel | 972
Italy | 39
Ivory Coast see Cote d'Ivorie | 225
Jamaica | 1 809
Japan | 81
Jordan | 962
Kazakhstan | 7
Kenya | 254
Kirghizstan | 7
Kiribati | 686
Korea (North) | 850
Korea (South) | 82
Kuwait | 965
Laos | 856
Latvia | 371
Lebanon | 961
Lesotho | 266
Liberia | 231
Lybia | 218
Lichtenstein | 41 75
Lithuania | 370
Luxembourg | 352
Macao | 853
Macedonia | 389
Madagascar | 261
Malawi | 265
Malaysia | 60
Maldives | 960
Mali | 223
Malta | 356
Marshall Islands | 692
Martinique | 596
Mauritania | 222
Mauritius | 230
Mayotte | 269
Mexico | 52
Micronesia | 691
Moldovia | 373
Monaco | 33 93
Mongolia | 976
Montserrat | 1 809
Morocco | 212
Mozanbique | 258
Myanmar (Burma) | 95
Namibia | 264
Nauru | 674
Napal | 977
Netherlands (Holland) | 31
Netherlands Antilles | 599
New Caledonia | 687
Nicaragua | 505
Niger | 227
Nigeria | 234
Norway | 47
Oman | 968
Pakistan | 92
Panama | 507
Papua New Guinea | 675
Paraguay | 595
Peru | 51
Philippines | 63
Pitcain Island | 649
Poland | 48
Portugal | 361
Pueto Ricco | 1 809
Qatar | 974
Romania | 40
Russia | 7
Rwanda | 250
St Helena | 290
St Kitts and Nevis | 1 809
Samoa (USA) | 685
Samoa Western | 685
San Marino | 378
Saudi Arabia | 966
Senegal | 221
Seychelles | 248
Sierra Leone | 232
Singapore | 65
Slovakia | 42
Slovenia | 386
Solom Islands | 677
Somalia | 252
South Africa | 27
Spain | 34
Sri Lanka | 94
Sudan | 249
Surinam | 597
Swaziland | 268
Sweden | 46
Switzerland | 41
Syria | 963
Taiwan | 886
Tajikistan | 7
Tanzania | 255
Thailand | 66
Togo | 228
Tongo | 676
Trinidad & Tobago | 1 809
Tunisia | 216
Turkey | 90
Turkmenistan | 7
Turks & Caicos | 1 809
Tuvalu | 688
Uganda | 256
Ukraine | 380
United Arab Emirates | 971
United Kingdom | 44
Uraguay | 598
USA | 1
Uzbekistan | 7
Vanuatu | 678
Venezuela | 58
Vietnam | 84
Virgin Islands (UK) | 1 809 49
Virgin Islands (US) | 1 809
Yemen | 967
Yugoslavia | 381
Zaire | 243
Zambia | 260
Zimbabwe | 263


ISO3166 National Codes

ad | Andorra
ae | United Arab Emirates
af | Afghanistan
ag | Antigua and Barbuda
ai | Anguilla
al | Albania
am | Armenia
an | Netherlands Antilles
ao | Angola
aq | Antarctica
ar | Argentina
as | American Samoa
at | Austria
au | Australia
aw | Aruba
az | Azerbaidjan
ba | Bosnia-Herzegovina
bb | Barbados
bd | Bangladesh
be | Belgium
bf | Burkina Faso
bg | Bulgaria
bh | Bahrain
bi | Burundi
bj | Benin
bm | Bermuda
bn | Brunei Darussalam
bo | Bolivia
br | Brazil
bs | Bahamas
bt | Bhutan
bv | Bouvet Island
bw | Botswana
by | Belarus
bz | Belize
ca | Canada
cc | Cocos (Keeling) Islands
cf | Central African Republic
cg | Congo
ch | Switzerland
ci | Ivory Coast (Cote D'Ivoire)
ck | Cook Islands
cl | Chile
cm | Cameroon
cn | China
co | Colombia
cr | Costa Rica
cs | Former Czechoslovakia
cu | Cuba
cv | Cape Verde
cx | Christmas Island
cy | Cyprus
cz | Czech Republic
de | Germany
dj | Djibouti
dk | Denmark
dm | Dominica
do | Dominican Republic
dz | Algeria
ec | Ecuador
ee | Estonia
eg | Egypt
eh | Western Sahara
er | Eritrea
es | Spain
et | Ethiopia
fi | Finland
fj | Fiji
fk | Falkland Islands
fm | Micronesia
fo | Faroe Islands
fr | France
fx | France (European Territory)
ga | Gabon
gb | Great Britain
gd | Grenada
ge | Georgia
gf | French Guyana
gh | Ghana
gi | Gibraltar
gl | Greenland
gm | Gambia
gn | Guinea
gp | Guadeloupe (French)
gq | Equatorial Guinea
gr | Greece
gs | S. Georgia & S. Sandwich Isls.
gt | Guatemala
gu | Guam (USA)
gw | Guinea Bissau
gy | Guyana
hk | Hong Kong
hm | Heard and McDonald Islands
hn | Honduras
hr | Croatia
ht | Haiti
hu | Hungary
id | Indonesia
ie | Ireland
il | Israel
in | India
io | British Indian Ocean Territory
iq | Iraq
ir | Iran
is | Iceland
it | Italy
jm | Jamaica
jo | Jordan
jp | Japan
ke | Kenya
kg | Kyrgyzstan
kh | Cambodia
ki | Kiribati
km | Comoros
kn | Saint Kitts & Nevis Anguilla
kp | North Korea
kr | South Korea
kw | Kuwait
ky | Cayman Islands
kz | Kazakhstan
la | Laos
lb | Lebanon
lc | Saint Lucia
li | Liechtenstein
lk | Sri Lanka
lr | Liberia
ls | Lesotho
lt | Lithuania
lu | Luxembourg
lv | Latvia
ly | Libya
ma | Morocco
mc | Monaco
md | Moldavia
mg | Madagascar
mh | Marshall Islands
mk | Macedonia
ml | Mali
mm | Myanmar
mn | Mongolia
mo | Macau
mp | Northern Mariana Islands
mq | Martinique (French)
mr | Mauritania
ms | Montserrat
mt | Malta
mu | Mauritius
mv | Maldives
mw | Malawi
mx | Mexico
my | Malaysia
mz | Mozambique
na | Namibia
nc | New Caledonia (French)
ne | Niger
nf | Norfolk Island
ng | Nigeria
ni | Nicaragua
nl | Netherlands
no | Norway
np | Nepal
nr | Nauru
nt | Neutral Zone
nu | Niue
nz | New Zealand
om | Oman
pa | Panama
pe | Peru
pf | Polynesia (French)
pg | Papua New Guinea
ph | Philippines
pk | Pakistan
pl | Poland
pm | Saint Pierre and Miquelon
pn | Pitcairn Island
pr | Puerto Rico
pt | Portugal
pw | Palau
py | Paraguay
qa | Qatar
re | Reunion (French)
ro | Romania
ru | Russian Federation
rw | Rwanda
sa | Saudi Arabia
sb | Solomon Islands
sc | Seychelles
sd | Sudan
se | Sweden
sg | Singapore
sh | Saint Helena
si | Slovenia
sj | Svalbard and Jan Mayen Islands
sk | Slovak Republic
sl | Sierra Leone
sm | San Marino
sn | Senegal
so | Somalia
sr | Suriname
st | Saint Tome (Sao Tome) and Principe
su | Former USSR
sv | El Salvador
sy | Syria
sz | Swaziland
tc | Turks and Caicos Islands
td | Chad
tf | French Southern Territories
tg | Togo
th | Thailand
tj | Tadjikistan
tk | Tokelau
tm | Turkmenistan
tn | Tunisia
to | Tonga
tp | East Timor
tr | Turkey
tt | Trinidad and Tobago
tv | Tuvalu
tw | Taiwan
tz | Tanzania
ua | Ukraine
ug | Uganda
uk | United Kingdom
um | USA Minor Outlying Islands
us | United States
uy | Uruguay
uz | Uzbekistan
va | Vatican City State
vc | Saint Vincent & Grenadines
ve | Venezuela
vg | Virgin Islands (British)
vi | Virgin Islands (USA)
vn | Vietnam
vu | Vanuatu
wf | Wallis and Futuna Islands
ws | Samoa
ye | Yemen
yt | Mayotte
yu | Yugoslavia
za | South Africa
zm | Zambia
zr | Zaire
zw | Zimbabwe


S-Lang console

The S-Lang console window shows all output from S-Lang scripts, including any errors found while compiling the startup script.

It can be shown and hidden from the Scripts option of the View menu

Typing a space into the script console will open a window where you can enter S-Lang code to be executed immediately


A Guide to the S-Lang Language

John E. Davis, davis@space.mit.edu

Sun Nov 14 02:05:11 1999

1. Preface

  • 1.1 A Brief History of S-Lang
  • 1.2 Acknowledgements

2. Introduction

  • 2.1 Language Features
  • 2.2 Data Types and Operators
  • 2.3 Statements and Functions
  • 2.4 Error Handling
  • 2.5 Run-Time Library
  • 2.6 Input/Output
  • 2.7 Obtaining S-Lang

3. Overview of the Language

  • 3.1 Variables and Functions
  • 3.2 Strings
  • 3.3 Referencing and Dereferencing
  • 3.4 Arrays
  • 3.5 Structures and User-Defined Types
  • 3.6 Namespaces

4. Data Types and Literal Constants

  • 4.1 Predefined Data Types
  • 4.2 Typecasting: Converting from one Type to Another

5. Identifiers

6. Variables

7. Operators

  • 7.1 Unary Operators
  • 7.2 Binary Operators
  • 7.3 Mixing Integer and Floating Point Arithmetic
  • 7.4 Short Circuit Boolean Evaluation

8. Statements

  • 8.1 Variable Declaration Statements
  • 8.2 Assignment Statements
  • 8.3 Conditional and Looping Statements
  • 8.4 break, return, continue

9. Functions

  • 9.1 Declaring Functions
  • 9.2 Parameter Passing Mechanism
  • 9.3 Referencing Variables
  • 9.4 Functions with a Variable Number of Arguments
  • 9.5 Returning Values
  • 9.6 Multiple Assignment Statement
  • 9.7 Exit-Blocks

10. Name Spaces

11. Arrays

  • 11.1 Creating Arrays
  • 11.2 Reshaping Arrays
  • 11.3 Indexing Arrays
  • 11.4 Arrays and Variables
  • 11.5 Using Arrays in Computations

12. Associative Arrays

13. Structures and User-Defined Types

  • 13.1 Defining a Structure
  • 13.2 Accessing the Fields of a Structure
  • 13.3 Linked Lists
  • 13.4 Defining New Types

14. Error Handling

  • 14.1 Error-Blocks
  • 14.2 Clearing Errors

15. Loading Files: evalfile and autoload

16. File Input/Output

  • 16.1 Input/Output via stdio
  • 16.2 POSIX I/O
  • 16.3 Advanced I/O techniques

17. Debugging

18. Regular Expressions

  • 18.1 S-Lang RE Syntax
  • 18.2 Differences between S-Lang and egrep REs

19. Future Directions

Appendix

20. Copyright

  • 20.1 The GNU Public License
  • 20.2 The Artistic License
Next Previous Contents


A Guide to the S-Lang Language: Preface

 Next Previous Contents

1. Preface

S-Lang is an interpreted language that was designed from the start to be easily embedded into a program to provide it with a powerful extension language. Examples of programs that use S-Lang as an extension language include the jed text editor, the slrn newsreader, and sldxe (unreleased), a numerical computation program. For this reason, S-Lang does not exist as a separate application and many of the examples in this document are presented in the context of one of the above applications.

S-Lang is also a programmer's library that permits a programmer to develop sophisticated platform-independent software. In addition to providing the S-Lang extension language, the library provides facilities for screen management, keymaps, low-level terminal I/O, etc. However, this document is concerned only with the extension language and does not address these other features of the S-Lang library. For information about the other components of the library, the reader is referred to the The S-Lang Library Reference.

1.1 A Brief History of S-Lang

I first began working on S-Lang sometime during the fall of 1992. At that time I was writing a text editor (jed), which I wanted to endow with a macro language. It occured to me that an application-independent language that could be embedded into the editor would prove more useful because I could envision embedding it into other programs. As a result, S-Lang was born.

S-Lang was originally a stack language that supported a postscript-like syntax. For that reason, I named it S-Lang, where the S was supposed to emphasize its stack-based nature. About a year later, I began to work on a preparser that would allow one to write using a more traditional infix syntax making it easier to use for those unfamiliar with stack based languages. Currently, the syntax of the language resembles C, nevertheless some postscript-like features still remain, e.g., the `%' character is still used as a comment delimiter.

1.2 Acknowledgements

Since I first released S-Lang, I have received a lot feedback about the library and the language from many people. This has given me the opportunity and pleasure to interact with several people to make the library portable and easy to use. In particular, I would like to thank the following individuals:

Luchesar Ionkov <lionkov@sf.cit.bg> for his comments and criticisms of the syntax of the language. He was the person who made me realize that the low-level byte-code engine should be totally type-independent. He also improved the tokenizer and preparser and impressed upon me that the language needed a grammar.

Mark Olesen <olesen@weber.me.queensu.ca> for his many patches to various aspects of the library and his support on AIX. He also contributed a lot to the pre-processing (SLprep) routines.

John Burnell <j.burnell@irl.cri.nz> for the OS/2 port of the video and keyboard routines. He also made value suggestions regarding the interpreter interface.

Darrel Hankerson <hankedr@mail.auburn.edu> for cleaning up and unifying some of the code and the makefiles.

Dominik Wujastyk <ucgadkw@ucl.ac.uk> who was always willing to test new releases of the library.

Michael Elkins <me@muddcs.cs.hmc.edu> for his work on the curses emulation.

Ulli Horlacher <framstag@belwue.de> and Oezguer Kesim <kesim@math.fu-berlin.de> for the S-Lang newsgroup and mailing list.

Hunter Goatley, Andy Harper <Andy.Harper@kcl.ac.uk>, and Martin P.J. Zinser <zinser@decus.decus.de> for their VMS support.

Dave Sims <sims@usa.acsys.com> and Chin Huang <cthuang@vex.net> for Windows 95 and Windows NT support.

Lloyd Zusman <ljz@asfast.com> and Rich Roth <rich@on-the-net.com> for creating and maintaining www.s-lang.org.

I am also grateful to many other people who send in bug-reports and bug-fixes, for without such community involvement, S-Lang would not be as well-tested and stable as it is. Finally, I would like to thank my wife for her support and understanding while I spent long weekend hours developing the library.

Next Previous Contents


A Guide to the S-Lang Language: Introduction

 Next Previous Contents

2. Introduction

S-Lang is a powerful interpreted language that may be embedded into an application to make the application extensible. This enables the application to be used in ways not envisioned by the programmer, thus providing the application with much more flexibility and power. Examples of applications that take advantage of the interpreter in this way include the jed editor and the slrn newsreader.

2.1 Language Features

The language features both global and local variables, branching and looping constructs, user-defined functions, structures, datatypes, and arrays. In addition, there is limited support for pointer types. The concise array syntax rivals that of commercial array-based numerical computing environments.

2.2 Data Types and Operators

The language provides built-in support for string, integer (signed and unsigned long and short), double precision floating point, and double precision complex numbers. In addition, it supports user defined structure types, multi-dimensional array types, and associative arrays. To facilitate the construction of sophisticated data structures such as linked lists and trees, a `reference' type was added to the language. The reference type provides much of the same flexibility as pointers in other languages. Finally, applications embedding the interpreter may also provide special application specific types, such as the Mark_Type that the jed editor provides.

The language provides standard arithmetic operations such as addition, subtraction, multiplication, and division. It also provides support for modulo arithmetic as well as operations at the bit level, e.g., exclusive-or. Any binary or unary operator may be extended to work with any data type. For example, the addition operator (+) has been extended to work between string types to permit string concatenation.

The binary and unary operators work transparently with array types. For example, if a and b are arrays, then a + b produces an array whose elements are the result of element by element addition of a and b. This permits one to do vector operations without explicitly looping over the array indices.

2.3 Statements and Functions

The S-Lang language supports several types of looping constructs and conditional statements. The looping constructs include while, do...while, for, forever, loop, foreach, and _for. The conditional statements include if, if-then-else, and !if.

User defined functions may be defined to return zero, one, or more values. Functions that return zero values are similar to `procedures' in languages such as PASCAL. The local variables of a function are always created on a stack allowing one to create recursive functions. Parameters to a function are always passed by value and never by reference. However, the language supports a reference data type that allows one to simulate pass by reference.

Unlike many interpreted languages, S-Lang allows functions to be dynamically loaded (function autoloading). It also provides constructs specifically designed for error handling and recovery as well as debugging aids (e.g., function tracebacks).

Functions and variables may be declared as private belonging to a namespace associated with the compilation unit that defines the function or variable. The ideas behind the namespace implementation stems from the C language and should be quite familiar to any one familiar with C.

2.4 Error Handling

The S-Lang language defines a construct called an error-block that may be used for error handling and recovery. When a non-fatal run-time error is encountered, any error blocks that have been defined are executed as the run-time stack unwinds. An error block can optionally clear the error and the program will continue running after the statement that triggered the error. This mechanism is somewhat similar to try-catch in C++.

2.5 Run-Time Library

Functions that compose the S-Lang run-time library are called intrinsics. Examples of S-Lang intrinsic functions available to every S-Lang application include string manipulation functions such as strcat, strchop, and strcmp. The S-Lang library also provides mathematical functions such as sin, cos, and tan; however, not all applications enable the use of these intrinsics. For example, to conserve memory, the 16 bit version of the jed editor does not provide support for any mathematics other than simple integer arithmetic, whereas other versions of the editor do support these functions.

Most applications embedding the languages will also provide a set of application specific intrinsic functions. For example, the jed editor adds over 100 application specific intrinsic functions to the language. Consult your application specific documentation to see what additional intrinsics are supported.

2.6 Input/Output

The language supports C-like stdio input/output functions such as fopen, fgets, fputs, and fclose. In addition it provides two functions, message and error, for writing to the standard output device and standard error. Specific applications may provide other I/O mechanisms, e.g., the jed editor supports I/O to files via the editor's buffers.

2.7 Obtaining S-Lang

Comprehensive information about the library may be obtained via the World Wide Web from http://www.s-lang.org.

S-Lang as well as some programs that embed it are freely available via anonymous ftp in the United States from

  • ftp://space.mit.edu/pub/davis.
It is also available outside the United States from the following mirror sites:
  • ftp://ftp.uni-stuttgart.de/pub/unix/misc/slang/
  • ftp://ftp.fu-berlin.de/pub/unix/news/slrn/
  • ftp://ftp.ntua.gr/pub/lang/slang/

The Usenet newsgroup alt.lang.s-lang was created for S-Lang programmers to exchange information and share macros for the various programs the embed the language. The newsgroup comp.editors can be a useful resource for S-Lang macros for the jed editor. Similarly, slrn users will find news.software.readers to be a valuable source of information.

Finally, two mailing lists dealing with the S-Lang library have been created:

  • slang-announce@babayaga.math.fu-berlin.de
  • slang-workers@babayaga.math.fu-berlin.de
The first list is for announcements of new releases of the library, while the second list is intended for those who use the library for their own code development. To subscribe to the announcement list, send an email to slang-announce-subscribe@babayaga.math.fu-berlin.de and include the word subscribe in the body of the message. To subscribe to the developers list, use the address slang-workers-subscribe@babayaga.math.fu-berlin.de.

Next Previous Contents


A Guide to the S-Lang Language: Overview of the Language

 Next Previous Contents

3. Overview of the Language

This purpose of this section is to give the reader a feel for the S-Lang language, its syntax, and its capabilities. The information and examples presented in this section should be sufficient to provide the reader with the necessary background to understand the rest of the document.

3.1 Variables and Functions

S-Lang is different from many other interpreted languages in the sense that all variables and functions must be declared before they can be used.

Variables are declared using the variable keyword, e.g.,

     variable x, y, z;
declares three variables, x, y, and z. Note the semicolon at the end of the statement. All S-Lang statements must end in a semi-colon.

Unlike compiled languages such as C, it is not necessary to specify the data type of a S-Lang variable. The data type of a S-Lang variable is determined upon assignment. For example, after execution of the statements

     x = 3;
     y = sin (5.6);
     z = "I think, therefore I am.";
x will be an integer, y will be a double, and z will be a string. In fact, it is even possible to re-assign x to a string:
     x = "x was an integer, but now is a string";
Finally, one can combine variable declarations and assignments in the same statement:
     variable x = 3, y = sin(5.6), z = "I think, therefore I am.";

Most functions are declared using the define keyword. A simple example is

      define compute_average (x, y)
      {
         variable s = x + y;
         return s / 2.0;
      }
which defines a function that simply computes the average of two numbers and returns the result. This example shows that a function consists of three parts: the function name, a parameter list, and the function body.

The parameter list consists of a comma separated list of variable names. It is not necessary to declare variables within a parameter list; they are implicitly declared. However, all other local variables used in the function must be declared. If the function takes no parameters, then the parameter list must still be present, but empty:

      define go_left_5 ()
      {
         go_left (5);
      }
The last example is a function that takes no arguments and returns no value. Some languages such as PASCAL distinguish such objects from functions that return values by calling these objects procedures. However, S-Lang, like C, does not make such a distinction.

The language permits recursive functions, i.e., functions that call themselves. The way to do this in S-Lang is to first declare the function using the form:

define function-name ();
It is not necessary to declare a parameter list when declaring a function in this way.

The most famous example of a recursive function is the factorial function. Here is how to implement it using S-Lang:

     define factorial ();   % declare it for recursion

     define factorial (n)
     {
        if (n < 2) return 1;
        return n * factorial (n - 1);
     }
This example also shows how to mix comments with code. S-Lang uses the `%' character to start a comment and all characters from the comment character to the end of the line are ignored.

3.2 Strings

Perhaps the most appealing feature of any interpreted language is that it frees the user from the responsibility of memory management. This is particularly evident when contrasting how S-Lang handles string variables with a lower level language such as C. Consider a function that concatenates three strings. An example in S-Lang is:

     define concat_3_strings (a, b, c)
     {
        return strcat (a, strcat (b, c));
     }
This function uses the built-in strcat function for concatenating two strings. In C, the simplest such function would look like:
     char *concat_3_strings (char *a, char *b, char *c)
     {
        unsigned int len;
        char *result;
        len = strlen (a) + strlen (b) + strlen (c);
        if (NULL == (result = (char *) malloc (len + 1)))
          exit (1);
        strcpy (result, a);
        strcat (result, b);
        strcat (result, c);
        return result;
     }
Even this C example is misleading since none of the issues of memory management of the strings has been dealt with. The S-Lang language hides all these issues from the user.

Binary operators have been defined to work with the string data type. In particular the + operator may be used to perform string concatenation. That is, one can use the + operator as an alternative to strcat:

      define concat_3_strings (a, b, c)
      {
         return a + b + c;
      }
See section ??? for more information about string variables.

3.3 Referencing and Dereferencing

The unary prefix operator, &, may be used to create a reference to an object, which is similar to a pointer in other languages. References are commonly used as a mechanism to pass a function as an argument to another function as the following example illustrates:

       define compute_functional_sum (funct)
       {
          variable i, sum;

          sum = 0;
          for (i = 0; i < 10; i++)
           {
              sum += @funct (i);
           }
          return sum;
       }

       variable sin_sum = compute_functional_sum (&sin);
       variable cos_sum = compute_functional_sum (&cos);
Here, the function compute_functional_sum applies the function specified by the parameter funct to the first 10 integers and returns the sum. The two statements following the function definition show how the sin and cos functions may be used.

Note the @ operator in the definition of compute_functional_sum. It is known as the dereference operator and is the inverse of the reference operator.

Another use of the reference operator is in the context of the fgets function. For example,

      define read_nth_line (file, n)
      {
         variable fp, line;
         fp = fopen (file, "r");

         while (n > 0)
           {
              if (-1 == fgets (&line, fp))
                return NULL;
              n--;
           }
         return line;
      }
uses the fgets function to read the nth line of a file. In particular, a reference to the local variable line is passed to fgets, and upon return line will be set to the character string read by fgets.

Finally, references may be used as an alternative to multiple return values by passing information back via the parameter list. The example involving fgets presented above provided an illustration of this. Another example is

       define set_xyz (x, y, z)
       {
          @x = 1;
          @y = 2;
          @z = 3;
       }
       variable X, Y, Z;
       set_xyz (&X, &Y, &Z);
which, after execution, results in X set to 1, Y set to 2, and Z set to 3. A C programmer will note the similarity of set_xyz to the following C implementation:
      void set_xyz (int *x, int *y, int *z)
      {
         *x = 1;
         *y = 2;
         *z = 3;
      }

3.4 Arrays

The S-Lang language supports multi-dimensional arrays of all datatypes. For example, one can define arrays of references to functions as well as arrays of arrays. Here are a few examples of creating arrays:

       variable A = Integer_Type [10];
       variable B = Integer_Type [10, 3];
       variable C = [1, 3, 5, 7, 9];
The first example creates an array of 10 integers and assigns it to the variable A. The second example creates a 2-d array of 30 integers arranged in 10 rows and 3 columns and assigns the result to B. In the last example, an array of 5 integers is assigned to the variable C. However, in this case the elements of the array are initialized to the values specified. This is known as an inline-array.

S-Lang also supports something called an range-array. An example of such an array is

      variable C = [1:9:2];
This will produce an array of 5 integers running from 1 through 9 in increments of 2.

Arrays are passed by reference to functions and never by value. This permits one to write functions which can initialize arrays. For example,

      define init_array (a)
      {
         variable i, imax;

         imax = length (a);
         for (i = 0; i < imax; i++)
           {
              a[i] = 7;
           }
      }

      variable A = Integer_Type [10];
      init_array (A);
creates an array of 10 integers and initializes all its elements to 7.

There are more concise ways of accomplishing the result of the previous example. These include:

      variable A = [7, 7, 7, 7, 7, 7, 7, 7, 7, 7];
      variable A = Integer_Type [10];  A[[0:9]] = 7;
      variable A = Integer_Type [10];  A[*] = 7;
The second and third methods use an array of indices to index the array A. In the second, the range of indices has been explicitly specified, whereas the third example uses a wildcard form. See section ??? for more information about array indexing.

Although the examples have pertained to integer arrays, the fact is that S-Lang arrays can be of any type, e.g.,

       variable A = Double_Type [10];
       variable B = Complex_Type [10];
       variable C = String_Type [10];
       variable D = Ref_Type [10];
create 10 element arrays of double, complex, string, and reference types, respectively. The last example may be used to create an array of functions, e.g.,
      D[0] = &sin;
      D[1] = &cos;

The language also defines unary, binary, and mathematical operations on arrays. For example, if A and B are integer arrays, then A + B is an array whose elements are the sum of the elements of A and B. A trivial example that illustrates the power of this capability is

        variable X, Y;
        X = [0:2*PI:0.01];
        Y = 20 * sin (X);
which is equivalent to the highly simplified C code:
        double *X, *Y;
        unsigned int i, n;

        n = (2 * PI) / 0.01 + 1;
        X = (double *) malloc (n * sizeof (double));
        Y = (double *) malloc (n * sizeof (double));
        for (i = 0; i < n; i++)
          {
            X[i] = i * 0.01;
            Y[i] = 20 * sin (X[i]);
          }

3.5 Structures and User-Defined Types

A structure is similar to an array in the sense that it is a container object. However, the elements of an array must all be of the same type (or of Any_Type), whereas a structure is heterogeneous. As an example, consider

      variable person = struct
      {
         first_name, last_name, age
      };
      variable bill = @person;
      bill.first_name = "Bill";
      bill.last_name = "Clinton";
      bill.age = 51;
In this example a structure consisting of the three fields has been created and assigned to the variable person. Then an instance of this structure has been created using the dereference operator and assigned to bill. Finally, the individual fields of bill were initialized. This is an example of an anonymous structure.

A named structure is really a new data type and may be created using the typedef keyword:

      typedef struct
      {
         first_name, last_name, age
      }
      Person_Type;

      variable bill = @Person_Type;
      bill.first_name = "Bill";
      bill.last_name = "Clinton";
      bill.age = 51;
The big advantage of creating a new type is that one can go on to create arrays of the data type
      variable People = Person_Type [100];
      People[0].first_name = "Bill";
      People[1].first_name = "Hillary";

The creation and initialization of a structure may be facilitated by a function such as

      define create_person (first, last, age)
      {
          variable person = @Person_Type;
          person.first_name = first;
          person.last_name = last;
          person.age = age;
          return person;
      }
      variable Bill = create_person ("Bill", "Clinton", 51);

Other common uses of structures is the creation of linked lists, binary trees, etc. For more information about these and other features of structures, see section ???.

3.6 Namespaces

In addition to the global namespace, each compilation unit (e.g., a file) is given a private namespace. A variable or function name that is declared using the static keyword will be placed in the private namespace associated with compilation unit. For example,

       variable i;
       static variable i;
defines two variables called i. The first declaration defines i in the global namespace, but the second declaration defines i in the private namespace.

The -> operator may be used in conjunction with the name of the namespace to access objects in the name space. In the above example, to access the variable i in the global namespace, one would use Global->i. Unless otherwise specified, a private namespace has no name and its objects may not be accessed from outside the compilation unit. However, the implements function may be used give the private namespace a name, allowing access to its objects. For example, if the file t.sl contains

      implements ("A");
      static variable i;
then another file may access the variable i via A->i.

Next Previous Contents


A Guide to the S-Lang Language: Data Types and Literal Constants

 Next Previous Contents

4. Data Types and Literal Constants

The current implementation of the S-Lang language permits up to 256 distinct data types, including predefined data types such as integer and floating point, as well as specialized applications specific data types. It is also possible to create new data types in the language using the typedef mechanism.

Literal constants are objects such as the integer 3 or the string "hello". The actual data type given to a literal constant depends upon the syntax of the constant. The following sections describe the syntax of literals of specific data types.

4.1 Predefined Data Types

The current version of S-Lang defines integer, floating point, complex, and string types. It also defines special purpose data types such as Null_Type, DataType_Type, and Ref_Type. These types are discussed below.

Integers

The S-Lang language supports both signed and unsigned characters, short integer, long integer, and plain integer types. On most 32 bit systems, there is no difference between an integer and a long integer; however, they may differ on 16 and 64 bit systems. Generally speaking, on a 16 bit system, plain integers are 16 bit quantities with a range of -32767 to 32767. On a 32 bit system, plain integers range from -2147483648 to 2147483647.

An plain integer literal can be specified in one of several ways:

  • As a decimal (base 10) integer consisting of the characters 0 through 9, e.g., 127. An integer specified this way cannot begin with a leading 0. That is, 0127 is not the same as 127.
  • Using hexadecimal (base 16) notation consisting of the characters 0 to 9 and A through F. The hexadecimal number must be preceded by the characters 0x. For example, 0x7F specifies an integer using hexadecimal notation and has the same value as decimal 127.
  • In Octal notation using characters 0 through 7. The Octal number must begin with a leading 0. For example, 0177 and 127 represent the same integer. Short, long, and unsigned types may be specified by using the proper suffixes: L indicates that the integer is a long integer, h indicates that the integer is a short integer, and U indicates that it is unsigned. For example, 1UL specifies an unsigned long integer. Finally, a character literal may be specified using a notation containing a character enclosed in single quotes as 'a'. The value of the character specified this way will lie in the range 0 to 256 and will be determined by the ASCII value of the character in quotes. For example,
                  i = '0';
    assigns to i the character 48 since the '0' character has an ASCII value of 48.

Any integer may be preceded by a minus sign to indicate that it is a negative integer.

Floating Point Numbers

Single and double precision floating point literals must contain either a decimal point or an exponent (or both). Here are examples of specifying the same double precision point number:

         12.    12.0    12e0   1.2e1   120e-1   .12e2   0.12e2
Note that 12 is not a floating point number since it contains neither a decimal point nor an exponent. In fact, 12 is an integer.

One may append the f character to the end of the number to indicate that the number is a single precision literal.

Complex Numbers

The language implements complex numbers as a pair of double precision floating point numbers. The first number in the pair forms the real part, while the second number forms the imaginary part. That is, a complex number may be regarded as the sum of a real number and an imaginary number.

Strictly speaking, the current implementation of the S-Lang does not support generic complex literals. However, it does support imaginary literals and a more generic complex number with a non-zero real part may be constructed from the imaginary literal via addition of a real number.

An imaginary literal is specified in the same way as a floating point literal except that i or j is appended. For example,

         12i    12.0i   12e0j
all represent the same imaginary number. Actually, 12i is really an imaginary integer except that S-Lang automatically promotes it to a double precision imaginary number.

A more generic complex number may be constructed from an imaginary literal via addition, e.g.,

        3.0 + 4.0i
produces a complex number whose real part is 3.0 and whose imaginary part is 4.0.

The intrinsic functions Real and Imag may be used to retrieve the real and imaginary parts of a complex number, respectively.

Strings

A string literal must be enclosed in double quotes as in:

      "This is a string".
Although there is no imposed limit on the length of a string, string literals must be less than 256 characters in length. It is possible to go beyond this limit by string concatenation, e.g.,
     "This is the first part of a long string"
       + "and this is the second half"
Any character except a newline (ASCII 10) or the null character (ASCII 0) may appear explicitly in a string literal. However, these characters may be used implicitly using the mechanism described below.

The backslash character is a special character and is used to include other special characters (such as a newline character) in the string. The special characters recognized are:

       \"    --  double quote
       \'    --  single quote
       \\    --  backslash
       \a    --  bell character (ASCII 7)
       \t    --  tab character (ASCII 9)
       \n    --  newline character (ASCII 10)
       \e    --  escape character (ASCII 27)
       \xhhh --  character expressed in HEXADECIMAL notation
       \ooo  --  character expressed in OCTAL notation
       \dnnn --  character expressed in DECIMAL
For example, to include the double quote character as part of the string, it must be preceded by a backslash character, e.g.,
       "This is a \"quote\""
Similarly, the next illustrates how a newline character may be included:
       "This is the first line\nand this is the second"

Null_Type

Objects of type Null_Type can have only one value: NULL. About the only thing that you can do with this data type is to assign it to variables and test for equality with other objects. Nevertheless, Null_Type is an important and extremely useful data type. Its main use stems from the fact that since it can be compared for equality with any other data type, it is ideal to represent the value of an object which does not yet have a value, or has an illegal value.

As a trivial example of its use, consider

      define add_numbers (a, b)
      {
         if (a == NULL) a = 0;
         if (b == NULL) b = 0;
         return a + b;
      }
      variable c = add_numbers (1, 2);
      variable d = add_numbers (1, NULL);
      variable e = add_numbers (1,);
      variable f = add_numbers (,);
It should be clear that after these statements have been executed, c will have a value of 3. It should also be clear that d will have a value of 1 because NULL has been passed as the second parameter. One feature of the language is that if a parameter has been omitted from a function call, the variable associated with that parameter will be set to NULL. Hence, e and f will be set to 1 and 0, respectively.

The Null_Type data type also plays an important role in the context of structures.

Ref_Type

Objects of Ref_Type are created using the unary reference operator &. Such objects may be dereferenced using the dereference operator @. For example,

      variable sin_ref = &sin;
      variable y = @sin_ref (1.0);
creates a reference to the sin function and assigns it to sin_ref. The second statement uses the dereference operator to call the function that sin_ref references.

The Ref_Type is useful for passing functions as arguments to other functions, or for returning information from a function via its parameter list. The dereference operator is also used to create an instance of a structure. For these reasons, further discussion of this important type can be found in section ??? and section ???.

Array_Type and Struct_Type

Variables of type Array_Type and Struct_Type are known as container objects. They are much more complicated than the simple data types discussed so far and each obeys a special syntax. For these reasons they are discussed in a separate chapters. See ???.

DataType_Type Type

S-Lang defines a type called DataType_Type. Objects of this type have values that are type names. For example, an integer is an object of type Integer_Type. The literals of DataType_Type include:

     Char_Type            (signed character)
     UChar_Type           (unsigned character)
     Short_Type           (short integer)
     UShort_Type          (unsigned short integer)
     Integer_Type         (plain integer)
     UInteger_Type        (plain unsigned integer)
     Long_Type            (long integer)
     ULong_Type           (unsigned long integer)
     Float_Type           (single precision real)
     Double_Type          (double precision real)
     Complex_Type         (complex numbers)
     String_Type          (strings, C strings)
     BString_Type         (binary strings)
     Struct_Type          (structures)
     Ref_Type             (references)
     Null_Type            (NULL)
     Array_Type           (arrays)
     DataType_Type        (data types)
as well as the names of any other types that an application defines.

The built-in function typeof returns the data type of its argument, i.e., a DataType_Type. For instance typeof(7) returns Integer_Type and typeof(Integer_Type) returns DataType_Type. One can use this function as in the following example:

      if (Integer_Type == typeof (x)) message ("x is an integer");
The literals of DataType_Type have other uses as well. One of the most common uses of these literals is to create arrays, e.g.,
        x = Complex_Type [100];
creates an array of 100 complex numbers and assigns it to x.

4.2 Typecasting: Converting from one Type to Another

Occasionally, it is necessary to convert from one data type to another. For example, if you need to print an object as a string, it may be necessary to convert it to a String_Type. The typecast function may be used to perform such conversions. For example, consider

      variable x = 10, y;
      y = typecast (x, Double_Type);
After execution of these statements, x will have the integer value 10 and y will have the double precision floating point value 10.0. If the object to be converted is an array, the typecast function will act upon all elements of the array. For example,
      variable x = [1:10];       % Array of integers
      variable y = typecast (x, Double_Type);
will create an array of 10 double precision values and assign it to y. One should also realize that it is not always possible to perform a typecast. For example, any attempt to convert an Integer_Type to a Null_Type will result in a run-time error.

Often the interpreter will perform implicit type conversions as necessary to complete calculations. For example, when multiplying an Integer_Type with a Double_Type, it will convert the Integer_Type to a Double_Type for the purpose of the calculation. Thus, the example involving the conversion of an array of integers to an array of doubles could have been performed by multiplication by 1.0, i.e.,

      variable x = [1:10];       % Array of integers
      variable y = 1.0 * x;

The string intrinsic function is similar to the typecast function except that it converts an object to a string representation. It is important to understand that a typecast from some type to String_Type is not the same as converting an object to its string operation. That is, typecast(x,String_Type) is not equivalent to string(x). The reason for this is that when given an array, the typecast function acts on each element of the array to produce another array, whereas the string function produces a a string.

The string function is useful for printing the value of an object. This use is illustrated in the following simple example:

      define print_object (x)
      {
         message (string (x));
      }
Here, the message function has been used because it writes a string to the display. If the string function was not used and the message function was passed an integer, a type-mismatch error would have resulted.

Next Previous Contents


A Guide to the S-Lang Language: Identifiers

 Next Previous Contents

5. Identifiers

The names given to variables, functions, and data types are called identifiers. There are some restrictions upon the actual characters that make up an identifier. An identifier name must start with a letter ([A-Za-z]), an underscore character, or a dollar sign. The rest of the characters in the name can be any combination of letters, digits, dollar signs, or underscore characters. However, all identifiers whose name begins with two underscore characters are reserved for internal use by the interpreter and declarations of objects with such names should be avoided.

Examples of valid identifiers include:

      mary    _3    _this_is_ok
      a7e1    $44   _44$_Three
However, the following are not legal:
      7abc   2e0    #xx
In fact, 2e0 actually specifies the real number 2.0.

Although the maximum length of identifiers is unspecified by the language, the length should be kept below 64 characters.

The following identifiers are reserved by the language for use as keywords:

        !if            _for        do         mod       sign       xor
        ERROR_BLOCK    abs         do_while   mul2      sqr        public
        EXIT_BLOCK     and         else       not       static     private
        USER_BLOCK0    andelse     exch       or        struct
        USER_BLOCK1    break       for        orelse    switch
        USER_BLOCK2    case        foreach    pop       typedef
        USER_BLOCK3    chs         forever    return    using
        USER_BLOCK4    continue    if         shl       variable
        __tmp          define      loop       shr       while

Next Previous Contents


A Guide to the S-Lang Language: Variables

 Next Previous Contents

6. Variables

A variable must be declared before it can be used, otherwise an undefined name error will be generated. A variable is declared using the variable keyword, e.g,

      variable x, y, z;
declares three variables, x, y, and z. This is an example of a variable declaration statement, and like all statements, it must end in a semi-colon.

Variables declared this way are untyped and inherit a type upon assignment. The actual type checking is performed at run-time. For example,

        x = "This is a string";
        x = 1.2;
        x = 3;
        x = 2i;
results in x being set successively to a string, a float, an integer, and to a complex number (0+2i). Any attempt to use a variable before it has acquired a type will result in an uninitialized variable error.

It is legal to put executable code in a variable declaration list. That is,

         variable x = 1, y = sin (x);
are legal variable declarations. This also provides a convenient way of initializing a variable.

Variables are classified as either global or local. A variable declared inside a function is said to be local and has no meaning outside the function. A variable is said to be global if it was declared outside a function. Global variables are further classified as being public, static, or private, according to the name space where they were defined. See chapter ??? for more information about name spaces.

The following global variables are predefined by the language and are mainly used as convenience variables:

      $0 $1 $2 $3 $4 $5 $6 $7 $8 $9

An intrinsic variable is another type of global variable. Such variables have a definite type which cannot be altered. Variables of this type may also be defined to be read-only, or constant variables. An example of an intrinsic variable is PI which is a read-only double precision variable with a value of approximately 3.14159265358979323846.

Next Previous Contents


A Guide to the S-Lang Language: Operators

 Next Previous Contents

7. Operators

S-Lang supports a variety of operators that are grouped into three classes: assignment operators, binary operators, and unary operators.

An assignment operator is used to assign a value to a variable. They will be discussed more fully in the context of the assignment statement in section ???.

An unary operator acts only upon a single quantity while a binary operation is an operation between two quantities. The boolean operator not is an example of an unary operator. Examples of binary operators include the usual arithmetic operators +, -, *, and /. The operator given by - can be either an unary operator (negation) or a binary operator (subtraction); the actual operation is determined from the context in which it is used.

Binary operators are used in algebraic forms, e.g., a + b. Unary operators fall in one of two classes: postfix-unary or prefix-unary. For example, in the expression -x, the minus sign is a prefix-unary operator.

Not all data types have binary or unary operations defined. For example, while String_Type objects support the + operator, they do not admit the * operator.

7.1 Unary Operators

The unary operators operate only upon a single operand. They include: not, ~, -, @, &, as well as the increment and decrement operators ++ and --, respectively.

The boolean operator not acts only upon integers and produces 0 if its operand is non-zero, otherwise it produces 1.

The bit-level not operator ~ performs a similar function, except that it operates on the individual bits of its integer operand.

The arithmetic negation operator - is the most well-known unary operator. It simply reverses the sign of its operand.

The reference (&) and dereference (@) operators will be discussed in greater detail in section ???. Similarly, the increment (++) and decrement (--) operators will be discussed in the context of the assignment operator.

7.2 Binary Operators

The binary operators may be grouped according to several classes: arithmetic operators, relational operators, boolean operators, and bitwise operators.

All binary and unary operators may be overloaded. For example, the arithmetic plus operator has been overloaded by the String_Type data type to permit concatenation between strings.

Arithmetic Operators

The arithmetic operators include +, -, *, /, which perform addition, subtraction, multiplication, and division, respectively. In addition to these, S-Lang supports the mod operator as well as the power operator ^.

The data type of the result produced by the use of one of these operators depends upon the data types of the binary participants. If they are both integers, the result will be an integer. However, if the operands are not of the same type, they will be converted to a common type before the operation is performed. For example, if one is a floating point value and the other is an integer, the integer will be converted to a float. In general, the promotion from one type to another is such that no information is lost, if possible. As an example, consider the expression 8/5 which indicates division of the integer 8 by the integer 5. The result will be the integer 1 and not the floating point value 1.6. However, 8/5.0 will produce 1.6 because 5.0 is a floating point number.

Relational Operators

The relational operators are >, >=, <, <=, ==, and !=. These perform the comparisons greater than, greater than or equal, less than, less than or equal, equal, and not equal, respectively. The result of one of these comparisons is the integer 1 if the comparison is true, or 0 if the comparison is false. For example, 6 >= 5 returns 1, but 6 == 5 produces 0.

Boolean Operators

There are only two boolean binary operators: or and and. These operators are defined only for integers and produce an integer result. The or operator returns 1 if either of its operands are non-zero, otherwise it produces 0. The and operator produces 1 if and only if both its operands are non-zero, otherwise it produces 0.

Neither of these operators perform the so-called boolean short-circuit evaluation. For example, consider the expression:

      (x != 0) and (1/x > 10)
Here, if x were to have a value of zero, a division by zero error would occur because even though x!=0 evaluates to zero, the and operator is not short-circuited and the 1/x expression would still be evaluated. Although these operators are not short-circuited, S-Lang does have another mechanism of performing short-circuit boolean evaluation via the orelse and andelse expressions. See below for information about these constructs.

Bitwise Operators

The bitwise binary operators are defined only with integer operands and are used for bit-level operations. Operators that fall in this class include &, |, shl, shr, and xor. The & operator performs a boolean AND operation between the corresponding bits of the operands. Similarly, the | operator performs the boolean OR operation on the bits. The bit-shifting operators shl and shr shift the bits of the first operand by the number given by the second operand to the left or right, respectively. Finally, the xor performs an EXCLUSIVE-OR operation.

These operators are commonly used to manipulate variables whose individual bits have distinct meanings. In particular, & is usually used to test bits, | can be used to set bits, and xor may be used to flip a bit.

As an example of using & to perform tests on bits, consider the following: The jed text editor stores some of the information about a buffer in a bitmapped integer variable. The value of this variable may be retrieved using the jed intrinsic function getbuf_info, which actually returns four quantities: the buffer flags, the name of the buffer, directory name, and file name. For the purposes of this section, only the buffer flags are of interest and can be retrieved via a function such as

      define get_buffer_flags ()
      {
         variable flags;
         (,,,flags) = getbuf_info ();
         return flags;
      }
The buffer flags is a bitmapped quantity where the 0th bit indicates whether or not the buffer has been modified, the first bit indicates whether or not autosave has been enabled for the buffer, and so on. Consider for the moment the task of determining if the buffer has been modified. This can be determined by looking at the zeroth bit, if it is 0 the buffer has not been modified, otherwise it has. Thus we can create the function,
     define is_buffer_modified ()
     {
        variable flags = get_buffer_flags ();
        return (flags & 1);
     }
where the integer 1 has been used since it has all of its bits set to 0, except for the zeroth one, which is set to 1. (At this point, it should also be apparent that bits are numbered from zero, thus an 8 bit integer consists of bits 0 to 7, where 0 is the least significant bit and 7 is the most significant one.) Similarly, we can create another function
     define is_autosave_on ()
     {
        variable flags = get_buffer_flags ();
        return (flags & 2);
     }
to determine whether or not autosave has been turned on for the buffer.

The shl operator may be used to form the integer with only the nth bit set. For example, 1 shl 6 produces an integer with all bits set to zero except the sixth bit, which is set to one. The following example exploits this fact:

     define test_nth_bit (flags, nth)
     {
        return flags & (1 shl nth);
     }

Namespace operator

The operator -> is used to in conjunction with the name of a namespace to access an object within the namespace. For example, if A is the name of a namespace containing the variable v, then A->v refers to that variable.

Operator Precedence

Binary Operators and Functions Returning Multiple Values

Care must be exercised when using binary operators with an operand the returns multiple values. In fact, the current implementation of the S-Lang language will produce incorrect results if both operands of a binary expression return multiple values. At most, only one of operands of a binary expression can return multiple values, and that operand must be the first one, not the second. For example,

    define read_line (fp)
    {
       variable line, status;

       status = fgets (&line, fp);
       if (status == -1)
         return -1;
       return (line, status);
    }
defines a function, read_line that takes a single argument, a handle to an open file, and returns one or two values, depending upon the return value of fgets. Now consider
        while (read_line (fp) > 0)
          {
             text = ();
             % Do something with text
             .
             .
          }
Here the relational binary operator > forms a comparison between one of the return values (the one at the top of the stack) and 0. In accordance with the above rule, since read_line returns multiple values, it occurs as the left binary operand. Putting it on the right as in
        while (0 < read_line (fp))    % Incorrect
          {
             text = ();
             % Do something with text
             .
             .
          }
violates the rule and will result in the wrong answer.

7.3 Mixing Integer and Floating Point Arithmetic

If a binary operation (+, -, * , /) is performed on two integers, the result is an integer. If at least one of the operands is a float, the other is converted to float and the result is float. For example:

      11 / 2           --> 5   (integer)
      11 / 2.0         --> 5.5 (float)
      11.0 / 2         --> 5.5 (float)
      11.0 / 2.0       --> 5.5 (float)
Finally note that only integers may be used as array indices, loop control variables, and bit operations. The conversion functions, int and float, may be used convert between floats and ints where appropriate, e.g.,
      int (1.5)         --> 1 (integer)
      float(1.5)        --> 1.5 (float)
      float (1)         --> 1.0 (float)

7.4 Short Circuit Boolean Evaluation

The boolean operators or and and are not short circuited as they are in some languages. S-Lang uses orelse and andelse expressions for short circuit boolean evaluation. However, these are not binary operators. Expressions of the form:

expr-1 and expr-2 and ... expr-n
can be replaced by the short circuited version using andelse:
andelse {expr-1} {expr-2} ... {expr-n}
A similar syntax holds for the orelse operator. For example, consider the statement:
      if ((x != 0) and (1/x > 10)) do_something ();
Here, if x were to have a value of zero, a division by zero error would occur because even though x!=0 evaluates to zero, the and operator is not short circuited and the 1/x expression would be evaluated causing division by zero. For this case, the andelse expression could be used to avoid the problem:
      if (andelse
          {x != 0}
          {1 / x > 10})  do_something ();

Next Previous Contents


A Guide to the S-Lang Language: Statements

 Next Previous Contents

8. Statements

Loosely speaking, a statement is composed of expressions that are grouped according to the syntax or grammar of the language to express a complete computation. Statements are analogous to sentences in a human language and expressions are like phrases. All statements in the S-Lang language must end in a semi-colon.

A statement that occurs within a function is executed only during execution of the function. However, statements that occur outside the context of a function are evaluated immediately.

The language supports several different types of statements such as assignment statements, conditional statements, and so forth. These are described in detail in the following sections.

8.1 Variable Declaration Statements

Variable declarations were already discussed in chapter ???. For the sake of completeness, a variable declaration is a statement of the form

variable variable-declaration-list ;
where the variable-declaration-list is a comma separated list of one or more variable names with optional initializations, e.g.,
     variable x, y = 2, z;

8.2 Assignment Statements

Perhaps the most well known form of statement is the assignment statement. Statements of this type consist of a left-hand side, an assignment operator, and a right-hand side. The left-hand side must be something to which an assignment can be performed. Such an object is called an lvalue.

The most common assignment operator is the simple assignment operator =. Simple of its use include

      x = 3;
      x = some_function (10);
      x = 34 + 27/y + some_function (z);
      x = x + 3;
In addition to the simple assignment operator, S-Lang also supports the assignment operators += and -=. Internally, S-Lang transforms
       a += b;
to
       a = a + b;
Similarly, a -= b is transformed to a = a - b. It is extremely important to realize that, in general, a+b is not equal to b+a. This means that a+=b is not the same as a=b+a. As an example consider
      a = "hello"; a += "world";
After execution of these two statements, a will have the value "helloworld" and not "worldhello".

Since adding or subtracting 1 from a variable is quite common, S-Lang also supports the unary increment and decrement operators ++, and --, respectively. That is, for numeric data types,

       x = x + 1;
       x += 1;
       x++;
are all equivalent. Similarly,
       x = x - 1;
       x -= 1;
       x--;
are also equivalent.

Strictly speaking, ++ and -- are unary operators. When used as x++, the ++ operator is said to be a postfix-unary operator. However, when used as ++x it is said to be a prefix-unary operator. The current implementation does not distinguish between the two forms, thus x++ and ++x are equivalent. The reason for this equivalence is that assignment expressions do not return a value in the S-Lang language as they do in C. Thus one should exercise care and not try to write C-like code such as

      x = 10;
      while (--x) do_something (x);     % Ok in C, but not in S-Lang
The closest valid S-Lang form involves a comma-expression:
      x = 10;
      while (x--, x) do_something (x);  % Ok in S-Lang and in C

S-Lang also supports a multiple-assignment statement. It is discussed in detail in section ???.

8.3 Conditional and Looping Statements

S-Lang supports a wide variety of conditional and looping statements. These constructs operate on statements grouped together in blocks. A block is a sequence of S-Lang statements enclosed in braces and may contain other blocks. However, a block cannot include function declarations. In the following, statement-or-block refers to either a single S-Lang statement or to a block of statements, and integer-expression is an integer-valued expression. next-statement represents the statement following the form under discussion.

Conditional Forms

if

The simplest condition statement is the if statement. It follows the syntax

if (integer-expression) statement-or-block next-statement
If integer-expression evaluates to a non-zero result, then the statement or group of statements implied statement-or-block will get executed. Otherwise, control will proceed to next-statement.

An example of the use of this type of conditional statement is

       if (x != 0)
         {
            y = 1.0 / x;
            if (x > 0) z = log (x);
         }
This example illustrates two if statements where the second if statement is part of the block of statements that belong to the first.

if-else

Another form of if statement is the if-else statement. It follows the syntax:

if (integer-expression) statement-or-block-1 else statement-or-block-2 next-statement
Here, if expression returns non-zero, statement-or-block-1 will get executed and control will pass on to next-statement. However, if expression returns zero, statement-or-block-2 will get executed before continuing with next-statement. A simple example of this form is
     if (x > 0) z = log (x); else error ("x must be positive");
Consider the more complex example:
     if (city == "Boston")
       if (street == "Beacon") found = 1;
     else if (city == "Madrid")
       if (street == "Calle Mayor") found = 1;
     else found = 0;
This example illustrates a problem that beginners have with if-else statements. The grammar presented above shows that the this example is equivalent to
     if (city == "Boston")
       {
         if (street == "Beacon") found = 1;
         else if (city == "Madrid")
           {
             if (street == "Calle Mayor") found = 1;
             else found = 0;
           }
       }
It is important to understand the grammar and not be seduced by the indentation!

!if

One often encounters if statements similar to

if (integer-expression == 0) statement-or-block
or equivalently,
if (not(integer-expression)) statement-or-block
The !if statement was added to the language to simplify the handling of such statements. It obeys the syntax
!if (integer-expression) statement-or-block
and is functionally equivalent to
if (not (expression)) statement-or-block

orelse, andelse

These constructs were discussed earlier. The syntax for the orelse statement is:

orelse {integer-expression-1} ... {integer-expression-n}
This causes each of the blocks to be executed in turn until one of them returns a non-zero integer value. The result of this statement is the integer value returned by the last block executed. For example,
     orelse { 0 } { 6 } { 2 } { 3 }
returns 6 since the second block is the first to return a non-zero result. The last two block will not get executed.

The syntax for the andelse statement is:

andelse {integer-expression-1} ... {integer-expression-n}
Each of the blocks will be executed in turn until one of them returns a zero value. The result of this statement is the integer value returned by the last block executed. For example,
     andelse { 6 } { 2 } { 0 } { 4 }
returns 0 since the third block will be the last to execute.

switch

The switch statement deviates the most from its C counterpart. The syntax is:

          switch (x)
            { ...  :  ...}
              .
              .
            { ...  :  ...}
The `:' operator is a special symbol which means to test the top item on the stack, and if it is non-zero, the rest of the block will get executed and control will pass out of the switch statement. Otherwise, the execution of the block will be terminated and the process will be repeated for the next block. If a block contains no : operator, the entire block is executed and control will pass onto the next statement following the switch statement. Such a block is known as the default case.

As a simple example, consider the following:

      switch (x)
        { x == 1 : print("Number is one.");}
        { x == 2 : print("Number is two.");}
        { x == 3 : print("Number is three.");}
        { x == 4 : print("Number is four.");}
        { x == 5 : print("Number is five.");}
        { print ("Number is greater than five.");}
Suppose x has an integer value of 3. The first two blocks will terminate at the `:' character because each of the comparisons with x will produce zero. However, the third block will execute to completion. Similarly, if x is 7, only the last block will execute in full.

A more familiar way to write the previous example used the case keyword:

      switch (x)
        { case 1 : print("Number is one.");}
        { case 2 : print("Number is two.");}
        { case 3 : print("Number is three.");}
        { case 4 : print("Number is four.");}
        { case 5 : print("Number is five.");}
        { print ("Number is greater than five.");}
The case keyword is a more useful comparison operator because it can perform a comparison between different data types while using == may result in a type-mismatch error. For example,
      switch (x)
        { (x == 1) or (x == "one") : print("Number is one.");}
        { (x == 2) or (x == "two") : print("Number is two.");}
        { (x == 3) or (x == "three") : print("Number is three.");}
        { (x == 4) or (x == "four") : print("Number is four.");}
        { (x == 5) or (x == "five") : print("Number is five.");}
        { print ("Number is greater than five.");}
will fail because the == operation is not defined between strings and integers. The correct way to write this to use the case keyword:
      switch (x)
        { case 1 or case "one" : print("Number is one.");}
        { case 2 or case "two" : print("Number is two.");}
        { case 3 or case "three" : print("Number is three.");}
        { case 4 or case "four" : print("Number is four.");}
        { case 5 or case "five" : print("Number is five.");}
        { print ("Number is greater than five.");}

Looping Forms

while

The while statement follows the syntax

while (integer-expression) statement-or-block next-statement
It simply causes statement-or-block to get executed as long as integer-expression evaluates to a non-zero result. For example,
      i = 10;
      while (i)
        {
          i--;
          newline ();
        }
will cause the newline function to get called 10 times. However,
      i = -10;
      while (i)
        {
          i--;
          newline ();
        }
would loop forever (or until i wraps from the most negative integer value to the most positive and then decrements to zero).

If you are a C programmer, do not let the syntax of the language seduce you into writing this example as you would in C:

      i = 10;
      while (i--) newline ();
The fact is that expressions such as i-- do not return a value in S-Lang as they do in C. If you must write this way, use the comma operator as in
      i = 10;
      while (i, i--) newline ();

do...while

The do...while statement follows the syntax

do statement-or-block while (integer-expression);
The main difference between this statement and the while statement is that the do...while form performs the test involving integer-expression after each execution of statement-or-block rather than before. This guarantees that statement-or-block will get executed at least once.

A simple example from the jed editor follows:

     bob ();      % Move to beginning of buffer
     do
       {
          indent_line ();
       }
     while (down (1));
This will cause all lines in the buffer to get indented via the jed intrinsic function indent_line.

for

Perhaps the most complex looping statement is the for statement; nevertheless, it is a favorite of many programmers. This statement obeys the syntax

for (init-expression; integer-expression; end-expression) statement-or-block next-statement
In addition to statement-or-block, its specification requires three other expressions. When executed, the for statement evaluates init-expression, then it tests integer-expression. If integer-expression returns zero, control passes to next-statement. Otherwise, it executes statement-or-block as long as integer-expression evaluates to a non-zero result. After every execution of statement-or-block, end-expression will get evaluated.

This statement is almost equivalent to

init-expression;
while (integer-expression)
{
statement-or-block
end-expression;
}
The reason that they are not fully equivalent involves what happens when statement-or-block contains a continue statement.

Despite the apparent complexity of the for statement, it is very easy to use. As an example, consider

     sum = 0;
     for (i = 1; i <= 10; i++) sum += i;
which computes the sum of the first 10 integers.

loop

The loop statement simply executes a block of code a fixed number of times. It follows the syntax

loop (integer-expression) statement-or-block next-statement
If the integer-expression evaluates to a positive integer, statement-or-block will get executed that many times. Otherwise, control will pass to next-statement.

For example,

      loop (10) newline ();
will cause the function newline to get called 10 times.

forever

The forever statement is similar to the loop statement except that it loops forever, or until a break or a return statement is executed. It obeys the syntax

forever statement-or-block
A trivial example of this statement is
     n = 10;
     forever
       {
          if (n == 0) break;
          newline ();
          n--;
       }

foreach

The foreach statement is used to loop over one or more statements for every element in a container object. A container object is a data type that consists of other types. Examples include both ordinary and associative arrays, structures, and strings. Every time through the loop the current member of the object is pushed onto the stack.

The simple type of foreach statement obeys the syntax

foreach (container-object) statement-or-block
Here container-object can be an expression that returns a container object. A simple example is
     foreach (["apple", "peach", "pear"])
      {
         fruit = ();
         process_fruit (fruit);
      }
This example shows that if the container object is an array, then successive elements of the array are pushed onto the stack prior to each execution cycle. If the container object is a string, then successive characters of the string are pushed onto the stack.

What actually gets pushed onto the stack may be controlled via the using form of the foreach statement. This more complex type of foreach statement follows the syntax

foreach ( container-object ) using ( control-list ) statement-or-block
The allowed values of control-list will depend upon the type of container object. For associative arrays (Assoc_Type), control-list specified whether keys, values, or both are pushed onto the stack. For example,
     foreach (a) using ("keys")
       {
          k = ();
           .
           .
       }
results in the keys of the associative array a being pushed on the list. However,
     foreach (a) using ("values")
       {
          v = ();
           .
           .
       }
will cause the values to be used, and
     foreach (a) using ("keys", "values")
       {
          (k,v) = ();
           .
           .
       }
will use both the keys and values of the array.

Similarly, for linked-lists of structures, one may walk the list via code like

     foreach (linked_list) using ("next")
       {
          s = ();
            .
            .
       }
This foreach statement is equivalent
     s = linked_list;
     while (s != NULL)
       {
          .
          .
         s = s.next;
       }
Consult the type-specific documentation for a discussion of the using control words, if any, appropriate for a given type.

8.4 break, return, continue

S-Lang also includes the non-local transfer functions return, break, and continue. The return statement causes control to return to the calling function while the break and continue statements are used in the context of loop structures. Consider:

       define fun ()
       {
          forever
            {
               s1;
               s2;
               ..
               if (condition_1) break;
               if (condition_2) return;
               if (condition_3) continue;
               ..
               s3;
            }
          s4;
          ..
       }
Here, a function fun has been defined that contains a forever loop consisting of statements s1, s2,...,s3, and three if statements. As long as the expressions condition_1, condition_2, and condition_3 evaluate to zero, the statements s1, s2,...,s3 will be repeatedly executed. However, if condition_1 returns a non-zero value, the break statement will get executed, and control will pass out of the forever loop to the statement immediately following the loop which in this case is s4. Similarly, if condition_2 returns a non-zero number, the return statement will cause control to pass back to the caller of fun. Finally, the continue statement will cause control to pass back to the start of the loop, skipping the statement s3 altogether.

Next Previous Contents


A Guide to the S-Lang Language: Functions

 Next Previous Contents

9. Functions

A function may be thought of as a group of statements that work together to perform a computation. While there are no imposed limits upon the number statements that may occur within a function, it is considered poor programming practice if a function contains many statements. This notion stems from the belief that a function should have a simple, well defined purpose.

9.1 Declaring Functions

Like variables, functions must be declared before they can be used. The define keyword is used for this purpose. For example,

      define factorial ();
is sufficient to declare a function named factorial. Unlike the variable keyword used for declaring variables, the define keyword does not accept a list of names.

Usually, the above form is used only for recursive functions. In most cases, the function name is almost always followed by a parameter list and the body of the function:

define function-name (parameter-list) { statement-list }
The function-name is an identifier and must conform to the naming scheme for identifiers discussed in chapter ???. The parameter-list is a comma-separated list of variable names that represent parameters passed to the function, and may be empty if no parameters are to be passed. The body of the function is enclosed in braces and consists of zero or more statements (statement-list).

The variables in the parameter-list are implicitly declared, thus, there is no need to declare them via a variable declaration statement. In fact any attempt to do so will result in a syntax error.

9.2 Parameter Passing Mechanism

Parameters to a function are always passed by value and never by reference. To see what this means, consider

     define add_10 (a)
     {
        a = a + 10;
     }
     variable b = 0;
     add_10 (b);
Here a function add_10 has been defined, which when executed, adds 10 to its parameter. A variable b has also been declared and initialized to zero before it is passed to add_10. What will be the value of b after the call to add_10? If S-Lang were a language that passed parameters by reference, the value of b would be changed to 10. However, S-Lang always passes by value, which means that b would retain its value of zero after the function call.

S-Lang does provide a mechanism for simulating pass by reference via the reference operator. See the next section for more details.

If a function is called with a parameter in the parameter list omitted, the corresponding variable in the function will be set to NULL. To make this clear, consider the function

     define add_two_numbers (a, b)
     {
        if (a == NULL) a = 0;
        if (b == NULL) b = 0;
        return a + b;
     }
This function must be called with two parameters. However, we can omit one or both of the parameters by calling it in one of the following ways:
     variable s = add_two_numbers (2,3);
     variable s = add_two_numbers (2,);
     variable s = add_two_numbers (,3);
     variable s = add_two_numbers (,);
The first example calls the function using both parameters; however, at least one of the parameters was omitted in the other examples. The interpreter will implicitly convert the last three examples to
     variable s = add_two_numbers (2, NULL);
     variable s = add_two_numbers (NULL, 3);
     variable s = add_two_numbers (NULL, NULL);
It is important to note that this mechanism is available only for function calls that specify more than one parameter. That is,
     variable s = add_10 ();
is not equivalent to add_10(NULL). The reason for this is simple: the parser can only tell whether or not NULL should be substituted by looking at the position of the comma character in the parameter list, and only function calls that indicate more than one parameter will use a comma. A mechanism for handling single parameter function calls is described in the next section.

9.3 Referencing Variables

One can achieve the effect of passing by reference by using the reference (&) and dereference (@) operators. Consider again the add_10 function presented in the previous section. This time we write it as

     define add_10 (a)
     {
        @a = @a + 10;
     }
     variable b = 0;
     add_10 (&b);
The expression &b creates a reference to the variable b and it is the reference that gets passed to add_10. When the function add_10 is called, the value of a will be a reference to b. It is only by dereferencing this value that b can be accessed and changed. So, the statement @a=@a+10; should be read `add 10' to the value of the object that a references and assign the result to the object that a references.

The reader familiar with C will note the similarity between references in S-Lang and pointers in C.

One of the main purposes for references is that this mechanism allows reference to functions to be passed to other functions. As a simple example from elementary calculus, consider the following function which returns an approximation to the derivative of another function at a specified point:

     define derivative (f, x)
     {
        variable h = 1e-6;
        return (@f(x+h) - @f(x)) / h;
     }
It can be used to differentiate the function
     define x_squared (x)
     {
        return x^2;
     }
at the point x = 3 via the expression derivative(&x_squared,3).

9.4 Functions with a Variable Number of Arguments

S-Lang functions may be defined to take a variable number of arguments. The reason for this is that the calling routine pushes the arguments onto the stack before making a function call, and it is up to the called function to pop the values off the stack and make assignments to the variables in the parameter list. These details are, for the most part, hidden from the programmer. However, they are important when a variable number of arguments are passed.

Consider the add_10 example presented earlier. This time it is written

     define add_10 ()
     {
        variable x;
        x = ();
        return x + 10;
     }
     variable s = add_10 (12);  % ==> s = 22;
For the uninitiated, this example looks as if it is destined for disaster. The add_10 function looks like it accepts zero arguments, yet it was called with a single argument. On top of that, the assignment to x looks strange. The truth is, the code presented in this example makes perfect sense, once you realize what is happening.

First, consider what happened when add_10 is called with the the parameter 12. Internally, 12 is pushed onto the stack and then the function called. Now, consider the function itself. x is a variable local to the function. The strange looking assignment `x=()' simply takes whatever is on the stack and assigns it to x. In other words, after this statement, the value of x will be 12, since 12 will be at the top of the stack.

A generic function of the form

    define function_name (x, y, ..., z)
    {
       .
       .
    }
is internally transformed by the interpreter to
    define function_name ()
    {
       variable x, y, ..., z;
       z = ();
       .
       .
       y = ();
       x = ();
       .
       .
    }
before further parsing. (The add_10 function, as defined above, is already in this form.) With this knowledge in hand, one can write a function that accepts a variable number of arguments. Consider the function:
    define average_n (n)
    {
       variable x, y;
       variable sum;

       if (n == 1)
         {
            x = ();
            sum = x;
         }
       else if (n == 2)
         {
            y = ();
            x = ();
            sum = x + y;
         }
       else error ("average_n: only one or two values supported");

       return sum / n;
   }
   variable ave1 = average_n (3.0, 1);        % ==> 3.0
   variable ave2 = average_n (3.0, 5.0, 2);   % ==> 4.0
Here, the last argument passed to average_n is an integer reflecting the number of quantities to be averaged. Although this example works fine, its principal limitation is obvious: it only supports one or two values. Extending it to three or more values by adding more else if constructs is rather straightforward but hardly worth the effort. There must be a better way, and there is:
   define average_n (n)
   {
      variable sum, x;
      sum = 0;
      loop (n)
        {
           x = ();    % get next value from stack
           sum += x;
        }
      return sum / n;
   }
The principal limitation of this approach is that one must still pass an integer that specifies how many values are to be averaged.

Fortunately, a special variable exists that is local to every function and contains the number of values that were passed to the function. That variable has the name _NARGS and may be used as follows:

   define average_n ()
   {
      variable x, sum = 0;

      if (_NARGS == 0) error ("Usage: ave = average_n (x, ...);");

      loop (_NARGS)
        {
           x = ();
           sum += x;
        }
      return sum / _NARGS;
   }
Here, if no arguments are passed to the function, a simple message that indicates how it is to be used is printed out.

9.5 Returning Values

As stated earlier, the usual way to return values from a function is via the return statement. This statement has the simple syntax

return expression-list ;
where expression-list is a comma separated list of expressions. If the function does not return any values, the expression list will be empty. As an example of a function that can return multiple values, consider
        define sum_and_diff (x, y)
        {
            variable sum, diff;

            sum = x + y;  diff = x - y;
            return sum, diff;
        }
which is a function returning two values.

It is extremely important to note that the calling routine must explicitly handle all values returned by a function. Although some languages such as C do not have this restriction, S-Lang does and it is a direct result of a S-Lang function's ability to return many values and accept a variable number of parameters. Examples of properly handling the above function include

       variable sum, diff;
       (sum, diff) = sum_and_diff (5, 4);  % ignore neither
       (sum, ) = sum_and_diff (5, 4);      % ignore diff
       (,) = sum_and_diff (5, 4);          % ignore both sum and diff
See the section below on assignment statements for more information about this important point.

9.6 Multiple Assignment Statement

S-Lang functions can return more than one value, e.g.,

       define sum_and_diff (x, y)
       {
          return x + y, x - y;
       }
returns two values. It accomplishes this by placing both values on the stack before returning. If you understand how S-Lang functions handle a variable number of parameters (section ???), then it should be rather obvious that one assigns such values to variables. One way is to use, e.g.,
      sum_and_diff (9, 4);
      d = ();
      s = ();

However, the most convenient way to accomplish this is to use a multiple assignment statement such as

       (s, d) = sum_and_diff (9, 4);
The most general form of the multiple assignment statement is
     ( var_1, var_2, ..., var_n ) = expression;
In fact, internally the interpreter transforms this statement into the form
     expression; var_n = (); ... var_2 = (); var_1 = ();
for further processing.

If you do not care about one of return values, simply omit the variable name from the list. For example,

        (s, ) = sum_and_diff (9, 4);
assigns the sum of 9 and 4 to s and the difference (9-4) will be removed from the stack.

As another example, the jed editor provides a function called down that takes an integer argument and returns an integer. It is used to move the current editing position down the number of lines specified by the argument passed to it. It returns the number of lines it successfully moved the editing position. Often one does not care about the return value from this function. Although it is always possible to handle the return value via

       variable dummy = down (10);
it is more convenient to use a multiple assignment expression and omit the variable name, e.g.,
       () = down (10);

Some functions return a variable number of values instead of a fixed number. Usually, the value at the top of the stack will indicate the actual number of return values. For such functions, the multiple assignment statement cannot directly be used. To see how such functions can be dealt with, consider the following function:

     define read_line (fp)
     {
        variable line;
        if (-1 == fgets (&line, fp))
          return -1;
        return (line, 0);
     }
This function returns either one or two values, depending upon the return value of fgets. Such a function may be handled as in the following example:
      status = read_line (fp);
      if (status != -1)
        {
           s = ();
           .
           .
        }
In this example, the last value returned by read_line is assigned to status and then tested. If it is non-zero, the second return value is assigned to s. In particular note the empty set of parenthesis in the assignment to s. This simply indicates that whatever is on the top of the stack when the statement is executed will be assigned to s.

Before leaving this section it is important to reiterate the fact that if a function returns a value, the caller must deal with that return value. Otherwise, the value will continue to live onto the stack and may eventually lead to a stack overflow error. Failing to handle the return value of a function is the most common mistake that inexperienced S-Lang programmers make. For example, the fflush function returns a value that many C programmer's never check. Instead of writing

      fflush (fp);
as one could in C, a S-Lang programmer should write
      () = fflush (fp);
in S-Lang. (Many good C programmer's write (void)fflush(fp) to indicate that the return value is being ignored).

9.7 Exit-Blocks

An exit-block is a set of statements that get executed when a functions returns. They are very useful for cleaning up when a function returns via an explicit call to return from deep within a function.

An exit-block is created by using the EXIT_BLOCK keyword according to the syntax

EXIT_BLOCK { statement-list }
where statement-list represents the list of statements that comprise the exit-block. The following example illustrates the use of an exit-block:
      define simple_demo ()
      {
         variable n = 0;

         EXIT_BLOCK { message ("Exit block called."); }

         forever
          {
            if (n == 10) return;
            n++;
          }
      }
Here, the function contains an exit-block and a forever loop. The loop will terminate via the return statement when n is 10. Before it returns, the exit-block will get executed.

A function can contain multiple exit-blocks, but only the last one encountered during execution will actually get executed. For example,

      define simple_demo (n)
      {
         EXIT_BLOCK { return 1; }

         if (n != 1)
           {
              EXIT_BLOCK { return 2; }
           }
         return;
      }
If 1 is passed to this function, the first exit-block will get executed because the second one would not have been encountered during the execution. However, if some other value is passed, the second exit-block would get executed. This example also illustrates that it is possible to explicitly return from an exit-block, although nested exit-blocks are illegal.

Next Previous Contents


A Guide to the S-Lang Language: Name Spaces

 Next Previous Contents

10. Name Spaces

By default, all global variables and functions are defined in the global namespace. In addition to the global namespace, every compilation unit (e.g., a file containing S-Lang code) has an anonymous namespace. Objects may be defined in the anonymous namespace via the static declaration keyword. For example,

     static variable x;
     static define hello () { message ("hello"); }
defines a variable x and a function hello in the anonymous namespace. This is useful when one wants to define functions and variables that are only to be used within the file, or more precisely the compilation unit, that defines them.

The implements function may be used to give the anonymous namespace a name to allow access to its objects from outside the compilation unit that defines them. For example,

     implements ("foo");
     static variable x;
allows the variable x to be accessed via foo->x, e.g.,
     if (foo->x == 1) foo->x = 2;

The implements function does more than simply giving the anonymous namespace a name. It also changes the default variable and function declaration mode from public to static. That is,

     implements ("foo");
     variable x;
and
     implements ("foo");
     static variable x;
are equivalent. Then to create a public object within the namespace, one must explicitly use the public keyword.

Finally, the private keyword may be used to create an object that is truly private within the compilation unit. For example,

    implements ("foo");
    variable x;
    private variable y;
allows x to be accessed from outside the namespace via foo->x, however y cannot be accessed.

Next Previous Contents


A Guide to the S-Lang Language: Arrays

 Next Previous Contents

11. Arrays

An array is a container object that can contain many values of one data type. Arrays are very useful objects and are indispensable for certain types of programming. The purpose of this chapter is to describe how arrays are defined and used in the S-Lang language.

11.1 Creating Arrays

The S-Lang language supports multi-dimensional arrays of all data types. Since the Array_Type is a data type, one can even have arrays of arrays. To create a multi-dimensional array of SomeType use the syntax

      SomeType [dim0, dim1, ..., dimN]
Here dim0, dim1, ... dimN specify the size of the individual dimensions of the array. The current implementation permits arrays consist of up to 7 dimensions. When a numeric array is created, all its elements are initialized to zero. The initialization of other array types depend upon the data type, e.g., String_Type and Struct_Type arrays are initialized to NULL.

As a concrete example, consider

     a = Integer_Type [10];
which creates a one-dimensional array of 10 integers and assigns it to a. Similarly,
     b = Double_Type [10, 3];
creates a 30 element array of double precision numbers arranged in 10 rows and 3 columns, and assigns it to b.

There is a more convenient syntax for creating and initializing a 1-d arrays. For example, to create an array of ten integers whose elements run from 1 through 10, one may simply use:

     a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
Similarly,
     b = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
specifies an array of ten doubles.

An even more compact way of specifying a numeric array is to use a range-array. For example,

     a = [0:9];
specifies an array of 10 integers whose elements range from 0 through 9. The most general form of a range array is
     [first-value : last-value : increment]
where the increment is optional and defaults to 1. This creates an array whose first element is first-value and whose successive values differ by increment. last-value sets an upper limit upon the last value of the array. The number of elements in the array is given by the expression
1 + (last-value - first-value)/increment

Another way to create an array is apply the dereference operator @ to the DataType_Type literal Array_Type. The actual syntax for this operation resembles a function call

variable a = @Array_Type (data-type, integer-array);
where data-type is of type DataType_Type and integer-array is a 1-d array of integers that specify the size of each dimension. For example,
     variable a = @Array_Type (Double_Type, [10, 20]);
will create a 10 by 20 array of doubles and assign it to a. This method of creating arrays derives its power from the fact that it is more flexible than the methods discussed in this section. We shall encounter it again in section ??? in the context of the array_info function.

11.2 Reshaping Arrays

It is sometimes possible to change the `shape' of an array using the reshape function. For example, a 1-d 10 element array may be reshaped into a 2-d array consisting of 5 rows and 2 columns. The only restriction on the operation is that the arrays must be commensurate. The reshape function follows the syntax

reshape (array-name, integer-array);
where array-name specifies the array to be reshaped to have the dimensions given by integer-array, a 1-dimensional array of integers. It is important to note that this does not create a new array, it simply reshapes the existing array. Thus,
       variable a = Double_Type [100];
       reshape (a, [10, 10]);
turns a into a 10 by 10 array.

11.3 Indexing Arrays

An individual element of an array may be referred to by its index. For example, a[0] specifies the zeroth element of the one dimensional array a, and b[3,2] specifies the element in the third row and second column of the two dimensional array b. As in C array indices are numbered from 0. Thus if a is a one-dimensional array of ten integers, the last element of the array is given by a[9]. Using a[10] would result in a range error.

A negative index may be used to index from the end of the array, with a[-1] referring to the last element of a, a[-2] referring to the next to the last element, and so on.

One may use the indexed value like any other variable. For example, to set the third element of an integer array to 6, use

     a[2] = 6;
Similarly, that element may be used in an expression, such as
     y = a[2] + 7;
Unlike other S-Lang variables which inherit a type upon assignment, array elements already have a type. For example, an attempt to assign a string value to an element of an integer array will result in a type-mismatch error.

One may use any integer expression to index an array. A simple example that computes the sum of the elements of 10 element 1-d array is

      variable i, sum;
      sum = 0;
      for (i = 0; i < 10; i++) sum += a[i];

Unlike many other languages, S-Lang permits arrays to be indexed by other integer arrays. Suppose that a is a 1-d array of 10 doubles. Now consider:

      i = [6:8];
      b = a[i];
Here, i is a 1-dimensional range array of three integers with i[0] equal to 6, i[1] equal to 7, and i[2] equal to 8. The statement b = a[i]; will create a 1-d array of three doubles and assign it to b. The zeroth element of b, b[0] will be set to the sixth element of a, or a[6], and so on. In fact, these two simple statements are equivalent to
     b = Double_Type [3];
     b[0] = a[6];
     b[1] = a[7];
     b[2] = a[8];
except that using an array of indices is not only much more convenient, but executes much faster.

More generally, one may use an index array to specify which elements are to participate in a calculation. For example, consider

     a = Double_Type [1000];
     i = [0:499];
     j = [500:999];
     a[i] = -1.0;
     a[j] = 1.0;
This creates an array of 1000 doubles and sets the first 500 elements to -1.0 and the last 500 to 1.0. Actually, one may do away with the i and j variables altogether and use
     a = Double_Type [1000];
     a [[0:499]] = -1.0;
     a [[500:999]] = 1.0;
It is important to understand the syntax used and, in particular, to note that a[[0:499]] is not the same as a[0:499]. In fact, the latter will generate a syntax error.

Often, it is convenient to use a rubber range to specify indices. For example, a[[500:]] specifies all elements of a whose index is greater than or equal to 500. Similarly, a[[:499]] specifies the first 500 elements of a. Finally, a[[:]] specifies all the elements of a; however, using a[*] is more convenient.

Now consider a multi-dimensional array. For simplicity, suppose that a is a 100 by 100 array of doubles. Then the expression a[0, *] specifies all elements in the zeroth row. Similarly, a[*, 7] specifies all elements in the seventh column. Finally, a[[3:5][6:12]] specifies the 3 by 7 region consisting of rows 3, 4, and 5, and columns 6 through 12 of a.

We conclude this section with a few examples.

Here is a function that computes the trace (sum of the diagonal elements) of a square 2 dimensional n by n array:

      define array_trace (a, n)
      {
         variable sum = 0, i;
         for (i = 0; i < n; i++) sum += a[i, i];
         return sum;
      }
This fragment creates a 10 by 10 integer array, sets its diagonal elements to 5, and then computes the trace of the array:
      a = Integer_Type [10, 10];
      for (j = 0; j < 10; j++) a[j, j] = 5;
      the_trace = array_trace(a, 10);
We can get rid of the for loop as follows:
      j = Integer_Type [10, 2];
      j[*,0] = [0:9];
      j[*,1] = [0:9];
      a[j] = 5;
Here, the goal was to construct a 2-d array of indices that correspond to the diagonal elements of a, and then use that array to index a. To understand how this works, consider the middle statements. They are equivalent to the following for loops:
      variable i;
      for (i = 0; i < 10; i++) j[i, 0] = i;
      for (i = 0; i < 10; i++) j[i, 1] = i;
Thus, row n of j will have the value (n,n), which is precisely what was sought.

Another example of this technique is the function:

      define unit_matrix (n)
      {
         variable a = Integer_Type [n, n];
         variable j = Integer_Type [n, 2];
         j[*,0] = [0:n - 1];
         j[*,1] = [0:n - 1];

         a[j] = 1;
         return a;
      }
This function creates an n by n unit matrix, that is a 2-d n by n array whose elements are all zero except on the diagonal where they have a value of 1.

11.4 Arrays and Variables

When an array is created and assigned to a variable, the interpreter allocates the proper amount of space for the array, initializes it, and then assigns to the variable a reference to the array. So, a variable that represents an array has a value that is really a reference to the array. This has several consequences, some good and some bad. It is believed that the advantages of this representation outweigh the disadvantages. First, we shall look at the positive aspects.

When a variable is passed to a function, it is always the value of the variable that gets passed. Since the value of a variable representing an array is a reference, a reference to the array gets passed. One major advantage of this is rather obvious: it is a fast and efficient way to pass the array. This also has another consequence that is illustrated by the function

      define init_array (a, n)
      {
         variable i;

         for (i = 0; i < n; i++) a[i] = some_function (i);
      }
where some_function is a function that generates a scalar value to initialize the ith element. This function can be used in the following way:
      variable X = Double_Type [100000];
      init_array (X, 100000);
Since the array is passed to the function by reference, there is no need to make a separate copy of the 100000 element array. As pointed out above, this saves both execution time and memory. The other salient feature to note is that any changes made to the elements of the array within the function will be manifested in the array outside the function. Of course, in this case, this is a desirable side-effect.

To see the downside of this representation, consider:

      variable a, b;
      a = Double_Type [10];
      b = a;
      a[0] = 7;
What will be the value of b[0]? Since the value of a is really a reference to the array of ten doubles, and that reference was assigned to b, b also refers to the same array. Thus any changes made to the elements of a, will also be made implicitly to b.

This begs the question: If the assignment of one variable which represents an array, to another variable results in the assignment of a reference to the array, then how does one make separate copies of the array? There are several answers including using an index array, e.g., b = a[*]; however, the most natural method is to use the dereference operator:

      variable a, b;
      a = Double_Type [10];
      b = @a;
      a[0] = 7;
In this example, a separate copy of a will be created and assigned to b. It is very important to note that S-Lang never implicitly dereferences an object. So, one must explicitly use the dereference operator. This means that the elements of a dereferenced array are not themselves dereferenced. For example, consider dereferencing an array of arrays, e.g.,
      variable a, b;
      a = Array_Type [2];
      a[0] = Double_Type [10];
      a[1] = Double_Type [10];
      b = @a;
In this example, b[0] will be a reference to the array that a[0] references because a[0] was not explicitly dereferenced.

11.5 Using Arrays in Computations

Many functions and operations work transparently with arrays. For example, if a and b are arrays, then the sum a + b is an array whose elements are formed from the sum of the corresponding elements of a and b. A similar statement holds for all other binary and unary operations.

Let's consider a simple example. Suppose, that we wish to solve a set of n quadratic equations whose coefficients are given by the 1-d arrays a, b, and c. In general, the solution of a quadratic equation will be two complex numbers. For simplicity, suppose that all we really want is to know what subset of the coefficients, a, b, c, correspond to real-valued solutions. In terms of for loops, we can write:

     variable i, d, index_array;
     index_array = Integer_Type [n];
     for (i = 0; i < n; i++)
       {
          d = b[i]^2 - 4 * a[i] * c[i];
          index_array [i] = (d >= 0.0);
       }
In this example, the array index_array will contain a non-zero value if the corresponding set of coefficients has a real-valued solution. This code may be written much more compactly and with more clarity as follows:
     variable index_array = ((b^2 - 4 * a * c) >= 0.0);

S-Lang has a powerful built-in function called where. This function takes an array of integers and returns a 2-d array of indices that correspond to where the elements of the input array are non-zero. This simple operation is extremely useful. For example, suppose a is a 1-d array of n doubles, and it is desired to set to zero all elements of the array whose value is less than zero. One way is to use a for loop:

     for (i = 0; i < n; i++)
       if (a[i] < 0.0) a[i] = 0.0;
If n is a large number, this statement can take some time to execute. The optimal way to achieve the same result is to use the where function:
     a[where (a < 0.0)] = 0;
Here, the expression (a < 0.0) returns an array whose dimensions are the same size as a but whose elements are either 1 or 0, according to whether or not the corresponding element of a is less than zero. This array of zeros and ones is then passed to where which returns a 2-d integer array of indices that indicate where the elements of a are less than zero. Finally, those elements of a are set to zero.

As a final example, consider once more the example involving the set of n quadratic equations presented above. Suppose that we wish to get rid of the coefficients of the previous example that generated non-real solutions. Using an explicit for loop requires code such as:

     variable i, j, nn, tmp_a, tmp_b, tmp_c;

     nn = 0;
     for (i = 0; i < n; i++)
       if (index_array [i]) nn++;

     tmp_a = Double_Type [nn];
     tmp_b = Double_Type [nn];
     tmp_c = Double_Type [nn];

     j = 0;
     for (i = 0; i < n; i++)
       {
          if (index_array [i])
            {
               tmp_a [j] = a[i];
               tmp_b [j] = b[i];
               tmp_c [j] = c[i];
               j++;
            }
       }
     a = tmp_a;
     b = tmp_b;
     c = tmp_c;
Not only is this a lot of code, it is also clumsy and error-prone. Using the where function, this task is trivial:
     variable i;
     i = where (index_array != 0);
     a = a[i];
     b = b[i];
     c = c[i];

All the examples up to now assumed that the dimensions of the array were known. Although the intrinsic function length may be used to get the total number of elements of an array, it cannot be used to get the individual dimensions of a multi-dimensional array. However, the function array_info may be used to get information about an array, such as its data type and size. The function returns three values: the data type, the number of dimensions, and an integer array containing the size of each dimension. It may be used to determine the number of rows of an array as follows:

     define num_rows (a)
     {
        variable dims, type, num_dims;

        (dims, num_dims, type) = array_info (a);
        return dims[0];
     }
The number of columns may be obtained in a similar manner:
     define num_cols (a)
     {
        variable dims, type, num_dims;

        (dims, num_dims, type) = array_info (a);
        if (num_dims > 1) return dims[1];
        return 1;
     }

Another use of array_info is to create an array that has the same number of dimensions as another array:

     define make_int_array (a)
     {
        variable dims, num_dims, type;

        (dims, num_dims, type) = array_info (a);
        return @Array_Type (Integer_Type, dims);
     }

Next Previous Contents


A Guide to the S-Lang Language: Associative Arrays

 Next Previous Contents

12. Associative Arrays

An associative array differs from an ordinary array in the sense that its size is not fixed and that is indexed by a string, called the key. For example, consider:

       variable A = Assoc_Type [Integer_Type];
       A["alpha"] = 1;
       A["beta"] = 2;
       A["gamma"] = 3;
Here, A represents an associative array of integers (Integer_Type) and three keys have been added to the array.

As the example suggests, an associative array may be created using one of the following forms:

Assoc_Type [type] Assoc_Type [type, default-value] Assoc_Type []
The last form returns an associative array of Any_Type objects allowing any type of object to may be stored in the array.

The form involving a default-value is useful for associating a default value for non-existent array members. This feature is explained in more detail below.

There are several functions that are specially designed to work with associative arrays. These include:

  • assoc_get_keys, which returns an ordinary array of strings containing the keys in the array.
  • assoc_get_values, which returns an ordinary array of the values of the associative array.
  • assoc_key_exists, which can be used to determine whether or not a key exists in the array.
  • assoc_delete_key, which may be used to remove a key (and its value) from the array.

To illustrate the use of an associative array, consider the problem of counting the number of repeated occurrences of words in a list. Let the word list be represented as an array of strings given by word_list. The number of occurrences of each word may be stored in an associative array as follows:

     variable a, word;
     a = Assoc_Type [Integer_Type];
     foreach (word_list)
       {
          word = ();
          if (0 == assoc_key_exists (a, word))
            a[word] = 0;
          a[word]++;  % same as a[word] = a[word] + 1;
       }
Note that assoc_key_exists was necessary to determine whether or not a word was already added to the array in order to properly initialize it. However, by creating the associative array with a default value of 0, the above code may be simplified to
     variable a, word;
     a = Assoc_Type [Integer_Type, 0];
     foreach (word_list)
       {
          word = ();
          a[word]++;
       }

Next Previous Contents


A Guide to the S-Lang Language: Structures and User-Defined Types

 Next Previous Contents

13. Structures and User-Defined Types

A structure is a heterogeneous container object, i.e., it is an object with elements whose values do not have to be of the same data type. The elements or fields of a structure are named, and one accesses a particular field of the structure via the field name. This should be contrasted with an array whose values are of the same type, and whose elements are accessed via array indices.

A user-defined data type is a structure with a fixed set of fields defined by the user.

13.1 Defining a Structure

The struct keyword is used to define a structure. The syntax for this operation is:

struct {field-name-1, field-name-2, ... field-name-N};
This creates and returns a structure with N fields whose names are specified by field-name-1, field-name-2, ..., field-name-N. When a structure is created, all its fields are initialized to NULL.

For example,

     variable t = struct { city_name, population, next };
creates a structure with three fields and assigns it to the variable t.

Alternatively, a structure may be created by dereferencing Struct_Type. For example, the above structure may also be created using one of the two forms:

      t = @Struct_Type ("city_name", "population", "next");
      t = @Struct_Type (["city_name", "population", "next"]);
These are useful when creating structures dynamically where one does not know the name of the fields until run-time.

Like arrays, structures are passed around via a references. Thus, in the above example, the value of t is a reference to the structure. This means that after execution of

     variable u = t;
both t and u refer to the same structure, since only the reference was used in the assignment. To actually create a new copy of the structure, use the dereference operator, e.g.,
     variable u = @t;

13.2 Accessing the Fields of a Structure

The dot (.) operator is used to specify the a particular field of structure. If s is a structure and field_name is a field of the structure, then s.field_name specifies that field of s. This specification can be used in expressions just like ordinary variables. Again, consider

     variable t = struct { city_name, population, next };
described in the last section. Then,
     t.city_name = "New York";
     t.population = 13000000;
     if (t.population > 200) t = t.next;
are all valid statements involving the fields of t.

13.3 Linked Lists

One of the most important uses of structures is to create a dynamic data structure such as a linked-list. A linked-list is simply a chain of structures that are linked together such that one structure in the chain is the value of a field of the previous structure in the chain. To be concrete, consider the structure discussed earlier:

     variable t = struct { city_name, population, next };
and suppose that we desire to create a list of such structures. The purpose of the next field is to provide the link to the next structure in the chain. Suppose that there exists a function, read_next_city, that reads city names and populations from a file. Then we can create the list via:
     define create_population_list ()
     {
        variable city_name, population, list_root, list_tail;
        variable next;

        list_root = NULL;
        while (read_next_city (&city_name, &population))
          {
             next = struct {city_name, population, next };

             next.city_name = city_name;
             next.population = population;
             next.next = NULL;

             if (list_root == NULL)
               list_root = next;
             else
               list_tail.next = next;

             list_tail = next;
          }
        return list_root;
     }
In this function, the variables list_root and list_tail represent the beginning and end of the list, respectively. As long as read_next_city returns a non-zero value, a new structure is created, initialized, and then appended to the list via the next field of the list_tail structure. On the first time through the loop, the list is created via the assignment to the list_root variable.

This function may be used as follows:

    variable Population_List = create_population_list ();
    if (Population_List == NULL) error ("List is empty");
We can create other functions that manipulate the list. An example is a function that finds the city with the largest population:
    define get_largest_city (list)
    {
       variable largest;

       largest = list;
       while (list != NULL)
         {
            if (list.population > largest.population)
              largest = list;
            list = list.next;
         }
       return largest.city_name;
    }

    vmessage ("%s is the largest city in the list",
               get_largest_city (Population_List)));
The get_largest_city is a typical example of how one traverses a linear linked-list by starting at the head of the list and successively moves to the next element of the list via the next field.

In the previous example, a while loop was used to traverse the linked list. It is faster to use a foreach loop for this:

    define get_largest_city (list)
    {
       variable largest, elem;

       largest = list;
       foreach (list)
         {
            elem = ();
            if (item.population > largest.population)
              largest = item;
         }
       return largest.city_name;
    }
Here a foreach loop has been used to walk the list via its next field. If the field name was not next, then it would have been necessary to use the using form of the foreach statement. For example, if the field name implementing the linked list was next_item, then
     foreach (list) using ("next_item")
     {
        elem = ();
        .
        .
     }
would have been used. In other words, unless otherwise indicated via the using clause, foreach walks the list using a field named next.

Now consider a function that sorts the list according to population. To illustrate the technique, a bubble-sort will be used, not because it is efficient, it is not, but because it is simple and intuitive.

    define sort_population_list (list)
    {
       variable changed;
       variable node, next_node, last_node;
       do
         {
            changed = 0;
            node = list;
            next_node = node.next;
            last_node = NULL;
            while (next_node != NULL)
              {
                 if (node.population < next_node.population)
                   {
                      % swap node and next_node
                      node.next = next_node.next;
                      next_node.next = node;
                      if (last_node != NULL)
                        last_node.next = next_node;

                      if (list == node) list = next_node;
                      node = next_node;
                      next_node = node.next;
                      changed++;
                   }
                 last_node = node;
                 node = next_node;
                 next_node = next_node.next;
              }
         }
       while (changed);

       return list;
    }
Note the test for equality between list and node, i.e.,
                      if (list == node) list = next_node;
It is important to appreciate the fact that the values of these variables are references to structures, and that the comparison only compares the references and not the actual structures they reference. If it were not for this, the algorithm would fail.

13.4 Defining New Types

A user-defined data type may be defined using the typedef keyword. In the current implementation, a user-defined data type is essentially a structure with a user-defined set of fields. For example, in the previous section a structure was used to represent a city/population pair. We can define a data type called Population_Type to represent the same information:

      typedef struct
      {
         city_name,
         population
      } Population_Type;
This data type can be used like all other data types. For example, an array of Population_Type types can be created,
      variable a = Population_Type[10];
and `populated' via expressions such as
      a[0].city_name = "Boston";
      a[0].population = 2500000;
The new type Population_Type may also be used with the typeof function:
      if (Population_Type = typeof (a)) city = a.city_name;
The dereference @ may be used to create an instance of the new type:
     a = @Population_Type;
     a.city_name = "Calcutta";
     a.population = 13000000;

Next Previous Contents


A Guide to the S-Lang Language: Error Handling

 Next Previous Contents

14. Error Handling

Many intrinsic functions signal errors in the event of failure. User defined functions may also generate an error condition via the error function. Depending upon the severity of the error, it can be caught and cleared using a construct called an error-block.

14.1 Error-Blocks

When the interpreter encounters a recoverable run-time error, it will return to top-level by unwinding its function call stack. Any error-blocks that it encounters as part of this unwinding process will get executed. Errors such as syntax errors and memory allocation errors are not recoverable, and error-blocks will not get executed when such errors are encountered.

An error-block is defined using the syntax

       ERROR_BLOCK { statement-list }
where statement-list represents a list of statements that comprise the error-block. A simple example of an error-block is
       define simple (a)
       {
          ERROR_BLOCK { message ("error-block executed"); }
          if (a) error ("Triggering Error");
          message ("hello");
       }
Executing this function via simple(0) will result in the message "hello". However, calling it using simple(1) will generate an error that will be caught, but not cleared, by the error-block and the "error-block executed" message will result.

Error-blocks are never executed unless triggered by an error. The only exception to this is when the user explicitly indicates that the error-block in scope should execute. This is indicated by the special keyword EXECUTE_ERROR_BLOCK. For example, simple could be recoded as

       define simple (a)
       {
          variable err_string = "error-block executed";
          ERROR_BLOCK { message (err_string); }
          if (a) error ("Triggering Error");
          err_string = "hello";
          EXECUTE_ERROR_BLOCK;
       }
Please note that EXECUTE_ERROR_BLOCK does not initiate an error condition; it simply causes the error-block to be executed and control will pass onto the next statement following the EXECUTE_ERROR_BLOCK statement.

14.2 Clearing Errors

Once an error has been caught by an error-block, the error can be cleared by the _clear_error function. After the error has been cleared, execution will resume at the next statement at the level of the error block following the statement that generated the error. For example, consider:

       define make_error ()
       {
           error ("Error condition created.");
           message ("This statement is not executed.");
       }

       define test ()
       {
           ERROR_BLOCK
             {
                _clear_error ();
             }
           make_error ();
           message ("error cleared.");
       }
Calling test will trigger an error in the make_error function, but will get cleared in the test function. The call-stack will unwind from make_error back into test where the error-block will get executed. As a result, execution resumes after the statement that makes the call to make_error since this statement is at the same level as the error-block that cleared the error.

Here is another example that illustrates how multiple error-blocks work:

       define example ()
       {
          variable n = 0, s = "";
          variable str;

          ERROR_BLOCK {
              str = sprintf ("s=%s,n=%d", s, n);
              _clear_error ();
          }

          forever
            {
              ERROR_BLOCK {
               s += "0";
               _clear_error ();
              }

              if (n == 0) error ("");

              ERROR_BLOCK {
               s += "1";
              }

              if (n == 1) error ("");
              n++;
            }
          return str;
       }
Here, three error-blocks have been declared. One has been declared outside the forever loop and the other two have been declared inside the forever loop. Each time through the loop, the variable n is incremented and a different error-block is triggered. The error-block that gets triggered is the last one encountered, since that will be the one in scope. On the first time through the loop, n will be zero and the first error-block in the loop will get executed. This error block clears the error and execution resumes following the if statement that triggered the error. The variable n will get incremented to 1 and, on the second cycle through the loop the second if statement will trigger an error causing the second error-block to execute. This time, the error is not cleared and the call-stack unwinds out of the forever loop, at which point the error-block outside the loop is in scope, causing it to execute. This error-block prints out the values of the variables s and n. It will clear the error and execution resumes on the statement following the forever loop. The result of this complicated series of events is that the function will return the string "s=01,n=1".

Next Previous Contents


A Guide to the S-Lang Language: Loading Files: evalfile and autoload

 Next Previous Contents

15. Loading Files: evalfile and autoload

Next Previous Contents


A Guide to the S-Lang Language: File Input/Output

 Next Previous Contents

16. File Input/Output

S-Lang provides built-in supports for two different I/O facilities. The simplest interface is modeled upon the C language stdio streams interface and consists of functions such as fopen, fgets, etc. The other interface is modeled on a lower level POSIX interface consisting of functions such as open, read, etc. In addition to permitting more control, the lower level interface permits one to access network objects as well as disk files.

16.1 Input/Output via stdio

Stdio Overview

The stdio interface consists of the following functions:

  • fopen, which opens a file for read or writing.
  • fclose, which closes a file opened by fopen.
  • fgets, used to read a line from the file.
  • fputs, which writes text to the file.
  • fprintf, used to write formatted text to the file.
  • fwrite, which may be used to write objects to the file.
  • fread, which reads a specified number of objects from the file.
  • feof, which is used to test whether the file pointer is at the end of the file.
  • ferror, which is used to see whether or not the stream associated with the file has an error.
  • clearerr, which clears the end-of-file and error indicators for the stream.
  • fflush, used to force all buffered data associated with the stream to be written out.
  • ftell, which is used to query the file position indicator of the stream.
  • fseek, which is used to set the position of the file position indicator of the stream.
  • fgetslines, which reads all the lines in a text file and returns them as an array of strings.

In addition, the interface supports the popen and pclose functions on systems where the corresponding C functions are available.

Before reading or writing to a file, it must first be opened using the fopen function. The only exceptions to this rule involves use of the pre-opened streams: stdin, stdout, and stderr. fopen accepts two arguments: a file name and a string argument that indicates how the file is to be opened, e.g., for reading, writing, update, etc. It returns a File_Type stream object that is used as an argument to all other functions of the stdio interface. Upon failure, it returns NULL. See the reference manual for more information about fopen.

Stdio Examples

In this section, some simple examples of the use of the stdio interface is presented. It is important to realize that all the functions of the interface return something, and that return value must be dealt with.

The first example involves writing a function to count the number of lines in a text file. To do this, we shall read in the lines, one by one, and count them:

    define count_lines_in_file (file)
    {
       variable fp, line, count;

       fp = fopen (file, "r");    % Open the file for reading
       if (fp == NULL)
         verror ("%s failed to open", file);

       count = 0;
       while (-1 != fgets (&line, fp))
         count++;

       () = fclose (fp);
       return count;
    }
Note that &line was passed to the fgets function. When fgets returns, line will contain the line of text read in from the file. Also note how the return value from fclose was handled.

Although the preceding example closed the file via fclose, there is no need to explicitly close a file because S-Lang will automatically close the file when it is no longer referenced. Since the only variable to reference the file is fp, it would have automatically been closed when the function returned.

Suppose that it is desired to count the number of characters in the file instead of the number of lines. To do this, the while loop could be modified to count the characters as follows:

      while (-1 != fgets (&line, fp))
        count += strlen (line);
The main difficulty with this approach is that it will not work for binary files, i.e., files that contain null characters. For such files, the file should be opened in binary mode via
      fp = fopen (file, "rb");
and then the data read in using the fread function:
      while (-1 != fread (&line, Char_Type, 1024, fp))
           count += bstrlen (line);
The fread function requires two additional arguments: the type of object to read (Char_Type in the case), and the number of such objects to read. The function returns the number of objects actually read, or -1 upon failure. The bstrlen function was used to compute the length of line because for Char_Type or UChar_Type objects, the fread function assigns a binary string (BString_Type) to line.

The foreach construct also works with File_Type objects. For example, the number of characters in a file may be counted via

     foreach (fp) using ("char")
     {
        ch = ();
        count++;
     }
To count the number of lines, one can use:
     foreach (fp) using ("line")
     {
        line = ();
        num_lines++;
        count += strlen (line);
     }

Finally, it should be mentioned that neither of these examples should be used to count the number of characters in a file when that information is more readily accessible by another means. For example, it is preferable to get this information via the stat_file function:

     define count_chars_in_file (file)
     {
        variable st;

        st = stat_file (file);
        if (st == NULL)
          error ("stat_file failed.");
        return st.st_size;
     }

16.2 POSIX I/O

16.3 Advanced I/O techniques

The previous examples illustrate how to read and write objects of a single data-type from a file, e.g.,

      num = fread (&a, Double_Type, 20, fp);
would result in a Double_Type[num] array being assigned to a if successful. However, suppose that the binary data file consists of numbers in a specified byte-order. How can one read such objects with the proper byte swapping? The answer is to use the fread function to read the objects as Char_Type and then unpack the resulting string into the specified data type, or types. This process is facilitated using the pack and unpack functions.

The pack function follows the syntax

BString_Type pack (format-string, item-list);
and combines the objects in the item-list according to format-string into a binary string and returns the result. Likewise, the unpack function may be used to convert a binary string into separate data objects:
(variable-list) = unpack (format-string, binary-string);

The format string consists of one or more data-type specification characters, and each may be followed by an optional decimal length specifier. Specifically, the data-types are specified according to the following table:

     c     char
     C     unsigned char
     h     short
     H     unsigned short
     i     int
     I     unsigned int
     l     long
     L     unsigned long
     j     16 bit int
     J     16 unsigned int
     k     32 bit int
     K     32 bit unsigned int
     f     float
     d     double
     F     32 bit float
     D     64 bit float
     s     character string, null padded
     S     character string, space padded
     x     a null pad character
A decimal length specifier may follow the data-type specifier. With the exception of the s and S specifiers, the length specifier indicates how many objects of that data type are to be packed or unpacked from the string. When used with the s or S specifiers, it indicates the field width to be used. If the length specifier is not present, the length defaults to one.

With the exception of c, C, s, S, and x, each of these may be prefixed by a character that indicates the byte-order of the object:

     >    big-endian order (network order)
     <    little-endian order
     =    native byte-order
The default is native byte order.

Here are a few examples that should make this more clear:

     a = pack ("cc", 'A', 'B');         % ==> a = "AB";
     a = pack ("c2", 'A', 'B');         % ==> a = "AB";
     a = pack ("xxcxxc", 'A', 'B');     % ==> a = "\0\0A\0\0B";
     a = pack ("h2", 'A', 'B');         % ==> a = "\0A\0B" or "\0B\0A"
     a = pack (">h2", 'A', 'B');        % ==> a = "\0\xA\0\xB"
     a = pack ("<h2", 'A', 'B');        % ==> a = "\0B\0A"
     a = pack ("s4", "AB", "CD");       % ==> a = "AB\0\0"
     a = pack ("s4s2", "AB", "CD");     % ==> a = "AB\0\0CD"
     a = pack ("S4", "AB", "CD");       % ==> a = "AB  "
     a = pack ("S4S2", "AB", "CD");     % ==> a = "AB  CD"

When unpacking, if the length specifier is greater than one, then an array of that length will be returned. In addition, trailing whitespace and null character are stripped when unpacking an object given by the S specifier. Here are a few examples:

    (x,y) = unpack ("cc", "AB");         % ==> x = 'A', y = 'B'
    x = unpack ("c2", "AB");             % ==> x = ['A', 'B']
    x = unpack ("x<H", "\0\xAB\xCD");    % ==> x = 0xCDABuh
    x = unpack ("xxs4", "a b c\0d e f");  % ==> x = "b c\0"
    x = unpack ("xxS4", "a b c\0d e f");  % ==> x = "b c"

Example: Reading /var/log/wtmp

Consider the task of reading the Unix system file /var/log/utmp, which contains login records about who logged onto the system. This file format is documented in section 5 of the online Unix man pages, and consists of a sequence of entries formatted according to the C structure utmp defined in the utmp.h C header file. The actual details of the structure may vary from one version of Unix to the other. For the purposes of this example, consider its definition under the Linux operating system running on an Intel processor:

    struct utmp {
       short ut_type;              /* type of login */
       pid_t ut_pid;               /* pid of process */
       char ut_line[12];           /* device name of tty - "/dev/" */
       char ut_id[2];              /* init id or abbrev. ttyname */
       time_t ut_time;             /* login time */
       char ut_user[8];            /* user name */
       char ut_host[16];           /* host name for remote login */
       long ut_addr;               /* IP addr of remote host */
    };
On this system, pid_t is defined to be an int and time_t is a long. Hence, a format specifier for the pack and unpack functions is easily constructed to be:
     "h i S12 S2 l S8 S16 l"
However, this particular definition is naive because it does not allow for structure padding performed by the C compiler in order to align the data types on suitable word boundaries. Fortunately, the intrinsic function pad_pack_format may be used to modify a format by adding the correct amount of padding in the right places. In fact, pad_pack_format applied to the above format on an Intel-based Linux system produces the result:
     "h x2 i S12 S2 x2 l S8 S16 l"
Here we see that 4 bytes of padding were added.

The other missing piece of information is the size of the structure. This is useful because we would like to read in one structure at a time using the fread function. Knowing the size of the various data types makes this easy; however it is even easier to use the sizeof_pack intrinsic function, which returns the size (in bytes) of the structure described by the pack format.

So, with all the pieces in place, it is rather straightforward to write the code:

    variable format, size, fp, buf;

    typedef struct
    {
       ut_type, ut_pid, ut_line, ut_id,
       ut_time, ut_user, ut_host, ut_addr
    } UTMP_Type;

    format = pad_pack_format ("h i S12 S2 l S8 S16 l");
    size = sizeof_pack (format);

    define print_utmp (u)
    {

      () = fprintf (stdout, "%-16s %-12s %-16s %s\n",
                    u.ut_user, u.ut_line, u.ut_host, ctime (u.ut_time));
    }

   fp = fopen ("/var/log/utmp", "rb");
   if (fp == NULL)
     error ("Unable to open utmp file");

   () = fprintf (stdout, "%-16s %-12s %-16s %s\n",
                          "USER", "TTY", "FROM", "LOGIN@");

   variable U = @UTMP_Type;

   while (-1 != fread (&buf, Char_Type, size, fp))
     {
        set_struct_fields (U, unpack (format, buf));
        print_utmp (U);
     }

   () = fclose (fp);
A few comments about this example are in order. First of all, note that a new data type called UTMP_Type was created, although this was not really necessary. We also opened the file in binary mode, but this too is optional under a Unix system where there is no distinction between binary and text modes. The print_utmp function does not print all of the structure fields. Finally, last but not least, the return values from fprintf and fclose were dealt with.

Next Previous Contents


A Guide to the S-Lang Language: Debugging

 Next Previous Contents

17. Debugging

The current implementation provides no support for an interactive debugger, although a future version will. Nevertheless, S-Lang has several features that aid the programmer in tracking down problems, including function call tracebacks and the tracing of function calls. However, the biggest debugging aid stems from the fact that the language is interpreted permitting one to easily add debugging statements to the code.

To enable debugging information, add the lines

    _debug_info = 1;
    _traceback = 1;
to the top of the source file of the code containing the bug and the reload the file. Setting the _debug_info variable to 1 causes line number information to be compiled into the functions when the file is loaded. The _traceback variable controls whether or not traceback information should be generated. If it is set to 1, the values of local variables will be dumped when the traceback is generated. Setting this variable to -1 will cause only function names to be reported in the traceback.

Here is an example of a traceback report:

    S-Lang Traceback: error
    S-Lang Traceback: verror
    S-Lang Traceback: (Error occurred on line 65)
    S-Lang Traceback: search_generic_search
      Local Variables:
        $0: Type: String_Type,  Value:  "Search forward:"
        $1: Type: Integer_Type, Value:  1
        $2: Type: Ref_Type,     Value:  _function_return_1
        $3: Type: String_Type,  Value:  "abcdefg"
        $4: Type: Integer_Type, Value:  1
    S-Lang Traceback: (Error occurred on line 72)
    S-Lang Traceback: search_forward
There are several ways to read this report; perhaps the simplest is to read it from the bottom. This report says that on line 72, the search_forward function called the search_generic_search function. On line 65 it called the verror function, which called error. The search_generic_search function contains 5 local variables and are represented symbolically as $0 through $4.

Next Previous Contents


A Guide to the S-Lang Language: Regular Expressions

 Next Previous Contents

18. Regular Expressions

The S-Lang library includes a regular expression (RE) package that may be used by an application embedding the library. The RE syntax should be familiar to anyone acquainted with regular expressions. In this section the syntax of the S-Lang regular expressions is discussed.

18.1 S-Lang RE Syntax

A regular expression specifies a pattern to be matched against a string, and has the property that the contcatenation of two REs is also a RE.

The S-Lang library supports the following standard regular expressions:

   .                 match any character except newline
   *                 matches zero or more occurences of previous RE
   +                 matches one or more occurences of previous RE
   ?                 matches zero or one occurence of previous RE
   ^                 matches beginning of a line
   $                 matches end of line
   [ ... ]           matches any single character between brackets.
                     For example, [-02468] matches `-' or any even digit.
                     and [-0-9a-z] matches `-' and any digit between 0 and 9
                     as well as letters a through z.
   \<                Match the beginning of a word.
   \>                Match the end of a word.
   \( ... \)
   \1, \2, ..., \9   Matches the match specified by nth \( ... \)
                     expression.
In addition the following extensions are also supported:
   \c                turn on case-sensitivity (default)
   \C                turn off case-sensitivity
   \d                match any digit
   \e                match ESC char
Here are some simple examples:

"^int " matches the "int " at the beginning of a line.

"\<money\>" matches "money" but only if it appears as a separate word.

"^$" matches an empty line.

A more complex pattern is

  "\(\<[a-zA-Z]+\>\)[ ]+\1\>"
which matches any word repeated consecutively. Note how the grouping operators \( and \) are used to define the text matched by the enclosed regular expression, and then subsequently referred to \1.

Finally, remember that when used in string literals either in the S-Lang language or in the C language, care must be taken to "double-up" the '\' character since both languages treat it as as an escape character.

18.2 Differences between S-Lang and egrep REs

There are several differences between S-Lang regular expressions and, e.g., egrep regular expressions.

The most notable difference is that the S-Lang regular expressions do not support the OR operator | in expressions. This means that "a|b" or "a\|b" do not have the meaning that they have in regular expression packages that support egrep-style expressions.

The other main difference is that while S-Lang regular expressions support the grouping operators \( and \), they are only used as a means of specifying the text that is matched. That is, the expression

     "@\([a-z]*\)@.*@\1@"
matches "xxx@abc@silly@abc@yyy", where the pattern \1 matches the text enclosed by the \( and \) expressions. However, in the current implementation, the grouping operators are not used to group regular expressions to form a single regular expression. Thus expression such as "\(hello\)*" is not a pattern to match zero or more occurances of "hello" as it is in e.g., egrep.

One question that comes up from time to time is why doesn't S-Lang simply employ some posix-compatible regular expression library. The simple answer is that, at the time of this writing, none exists that is is available across all the platforms that the S-Lang library supports (Unix, VMS, OS/2, win32, win16, BEOS, MSDOS, and QNX) and can be distributed under both the GNU and Artistic licenses. It is particularly important that the library and the interpreter support a common set of regular expressions in a platform independent manner.

Next Previous Contents


A Guide to the S-Lang Language: Future Directions

 Next Previous Contents

19. Future Directions

Several new features or enhancements to the S-Lang language are planned for the next major release. In no particular order, these include:

  • An interactive debugging facility.
  • Function qualifiers. These entities should already be familiar to VMS users or to those who are familiar with the IDL language. Basically, a qualifier is an optional argument that is passed to a function, e.g., plot(X,Y,/logx). Here /logx is a qualifier that specifies that the plot function should use a log scale for x.
  • File local variables and functions. A file local variable or function is an object that is global to the file that defines it.
  • Multi-threading. Currently the language does not support multiple threads.

Next Previous Contents


A Guide to the S-Lang Language: Copyright

 Next Previous Contents

20. Copyright

The S-Lang library is distributed under two copyrights: the GNU Genral Public License, and the Artistic License. Any program that uses the interpreter must adhere to rules of one of these licenses.

20.1 The GNU Public License

                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
                          675 Mass Ave, Cambridge, MA 02139, USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.

To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.

Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.

Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.

The precise terms and conditions for copying, distribution and modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.

1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.

You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.

2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.

In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.

4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.

6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.

7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.

This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.

8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.

9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.

10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs

If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) 19yy  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) 19yy name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice
This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.

20.2 The Artistic License

                         The "Artistic License"

                                Preamble
The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications.

Definitions:

        "Package" refers to the collection of files distributed by the
        Copyright Holder, and derivatives of that collection of files
        created through textual modification.

        "Standard Version" refers to such a Package if it has not been
        modified, or has been modified in accordance with the wishes
        of the Copyright Holder as specified below.

        "Copyright Holder" is whoever is named in the copyright or
        copyrights for the package.

        "You" is you, if you're thinking about copying or distributing
        this Package.

        "Reasonable copying fee" is whatever you can justify on the
        basis of media cost, duplication charges, time of people involved,
        and so on.  (You will not be required to justify it to the
        Copyright Holder, but only to the computing community at large
        as a market that must bear the fee.)

        "Freely Available" means that no fee is charged for the item
        itself, though there may be fees involved in handling the item.
        It also means that recipients of the item may redistribute it
        under the same conditions they received it.
1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers.

2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version.

3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following:

    a) place your modifications in the Public Domain or otherwise make them
    Freely Available, such as by posting said modifications to Usenet or
    an equivalent medium, or placing the modifications on a major archive
    site such as uunet.uu.net, or by allowing the Copyright Holder to include
    your modifications in the Standard Version of the Package.

    b) use the modified Package only within your corporation or organization.

    c) rename any non-standard executables so the names do not conflict
    with standard executables, which must also be provided, and provide
    a separate manual page for each non-standard executable that clearly
    documents how it differs from the Standard Version.

    d) make other distribution arrangements with the Copyright Holder.
4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following:
    a) distribute a Standard Version of the executables and library files,
    together with instructions (in the manual page or equivalent) on where
    to get the Standard Version.

    b) accompany the distribution with the machine-readable source of
    the Package with your modifications.

    c) give non-standard executables non-standard names, and clearly
    document the differences in manual pages (or equivalent), together
    with instructions on where to get the Standard Version.

    d) make other distribution arrangements with the Copyright Holder.
5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded.

6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package.

7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language.

8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package.

9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission.

10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0

John E. Davis, davis@space.mit.edu

Sun Nov 14 02:05:33 1999

1. Array Functions

  • 1.1 _reshape
  • 1.2 array_info
  • 1.3 array_map
  • 1.4 array_sort
  • 1.5 init_char_array
  • 1.6 length
  • 1.7 reshape
  • 1.8 transpose
  • 1.9 where

2. Associative Array Functions

  • 2.1 assoc_delete_key
  • 2.2 assoc_get_keys
  • 2.3 assoc_get_values
  • 2.4 assoc_key_exists

3. Functions that Operate on Strings

  • 3.1 Sprintf
  • 3.2 create_delimited_string
  • 3.3 extract_element
  • 3.4 is_list_element
  • 3.5 is_substr
  • 3.6 make_printable_string
  • 3.7 sprintf
  • 3.8 sscanf
  • 3.9 str_delete_chars
  • 3.10 str_quote_string
  • 3.11 str_replace
  • 3.12 str_uncomment_string
  • 3.13 strcat
  • 3.14 strchop
  • 3.15 strchopr
  • 3.16 strcmp
  • 3.17 strcompress
  • 3.18 string_match
  • 3.19 string_match_nth
  • 3.20 strjoin
  • 3.21 strlen
  • 3.22 strlow
  • 3.23 strncmp
  • 3.24 strsub
  • 3.25 strtok
  • 3.26 strtrans
  • 3.27 strtrim
  • 3.28 strtrim_beg
  • 3.29 strtrim_end
  • 3.30 strup
  • 3.31 substr

4. Functions that Manipulate Structures

  • 4.1 _push_struct_field_values
  • 4.2 get_struct_field
  • 4.3 get_struct_field_names
  • 4.4 set_struct_field
  • 4.5 set_struct_fields

5. Informational Functions

  • 5.1 _NARGS
  • 5.2 __get_defined_symbols
  • 5.3 __is_initialized
  • 5.4 _apropos
  • 5.5 _function_name
  • 5.6 _slang_version
  • 5.7 _slang_version_string
  • 5.8 get_doc_string_from_file
  • 5.9 is_defined

6. Mathematical Functions

  • 6.1 Conj
  • 6.2 Imag
  • 6.3 Real
  • 6.4 abs
  • 6.5 acos
  • 6.6 acosh
  • 6.7 asin
  • 6.8 asinh
  • 6.9 atan
  • 6.10 atanh
  • 6.11 cos
  • 6.12 cosh
  • 6.13 exp
  • 6.14 log
  • 6.15 log10
  • 6.16 mul2
  • 6.17 polynom
  • 6.18 set_float_format
  • 6.19 sign
  • 6.20 sin
  • 6.21 sinh
  • 6.22 sqr
  • 6.23 sqrt
  • 6.24 tan
  • 6.25 tanh

7. Message and Error Functions

  • 7.1 error
  • 7.2 message
  • 7.3 usage
  • 7.4 verror
  • 7.5 vmessage

8. Time and Date Functions

  • 8.1 _time
  • 8.2 ctime
  • 8.3 gmtime
  • 8.4 localtime
  • 8.5 tic
  • 8.6 time
  • 8.7 times
  • 8.8 toc

9. Data-Type Conversion Functions

  • 9.1 _slang_guess_type
  • 9.2 _typeof
  • 9.3 atof
  • 9.4 char
  • 9.5 define_case
  • 9.6 double
  • 9.7 int
  • 9.8 integer
  • 9.9 isdigit
  • 9.10 string
  • 9.11 tolower
  • 9.12 toupper
  • 9.13 typecast
  • 9.14 typeof

10. Stdio File I/O Functions

  • 10.1 clearerr
  • 10.2 fclose
  • 10.3 fdopen
  • 10.4 feof
  • 10.5 ferror
  • 10.6 fflush
  • 10.7 fgets
  • 10.8 fgetslines
  • 10.9 fopen
  • 10.10 fprintf
  • 10.11 fputs
  • 10.12 fread
  • 10.13 fseek
  • 10.14 ftell
  • 10.15 fwrite
  • 10.16 pclose
  • 10.17 popen
  • 10.18 printf

11. Low-level POSIX I/O functions

  • 11.1 close
  • 11.2 fileno
  • 11.3 isatty
  • 11.4 lseek
  • 11.5 open
  • 11.6 read
  • 11.7 write

12. Directory Functions

  • 12.1 chdir
  • 12.2 chmod
  • 12.3 chown
  • 12.4 getcwd
  • 12.5 listdir
  • 12.6 lstat_file
  • 12.7 mkdir
  • 12.8 readlink
  • 12.9 remove
  • 12.10 rename
  • 12.11 rmdir
  • 12.12 stat_file
  • 12.13 stat_is

13. Functions that parse pathnames

  • 13.1 path_basename
  • 13.2 path_concat
  • 13.3 path_dirname
  • 13.4 path_extname
  • 13.5 path_is_absolute

14. System Call Functions

  • 14.1 errno
  • 14.2 errno_string
  • 14.3 getegid
  • 14.4 geteuid
  • 14.5 getgid
  • 14.6 getpid
  • 14.7 getppid
  • 14.8 getuid
  • 14.9 kill
  • 14.10 mkfifo
  • 14.11 setgid
  • 14.12 setpgid
  • 14.13 setuid
  • 14.14 sleep
  • 14.15 system
  • 14.16 umask
  • 14.17 uname

15. Eval Functions

  • 15.1 autoload
  • 15.2 byte_compile_file
  • 15.3 eval
  • 15.4 evalfile

16. Module Functions

  • 16.1 get_import_module_path
  • 16.2 import
  • 16.3 set_import_module_path

17. Debugging Functions

  • 17.1 _clear_error
  • 17.2 _debug_info
  • 17.3 _slangtrace
  • 17.4 _trace_function
  • 17.5 _traceback

18. Stack Functions

  • 18.1 __pop_args
  • 18.2 __push_args
  • 18.3 _pop_n
  • 18.4 _print_stack
  • 18.5 _stk_reverse
  • 18.6 _stk_roll
  • 18.7 _stkdepth
  • 18.8 dup
  • 18.9 exch
  • 18.10 pop

19. Miscellaneous Functions

  • 19.1 __get_reference
  • 19.2 __uninitialize
  • 19.3 _auto_declare
  • 19.4 getenv
  • 19.5 implements
  • 19.6 putenv
Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Array Functions

 Next Previous Contents

1. Array Functions

1.1 _reshape

Synopsis

Copy an array to a new shape

Usage

Array_Type _reshape (Array_Type A, Array_Type I)

Description

The _reshape function creates a copy of an array A, reshapes it to the form specified by I and returns the result. The elements of I specify the new dimensions of the copy of A and must be consistent with the number of elements A.

Example

If A is a 100 element 1-d array, a new array 2-d array of size 20 by 5 may be created from the elements of A by

      A = _reshape (A, [20, 5]);
In this example, the original array was no longer needed. Hence, it is preferable to make use of the __tmp operator to avoid the creation of a new array, i.e.,
      A = _reshape (__tmp(A), [20,5]);
Notes

The reshape function performs a similar function to _reshape. In fact, the _reshape function could have been implemented via:

     define _reshape (a, i)
     {
        a = @a;     % Make a new copy
        reshape (a, i);
        return a;
     }
See Also

reshape, array_info

1.2 array_info

Synopsis

Returns information about an array

Usage

(Array_Type, Integer_Type, DataType_Type) array_info (Array_Type a)

Description

The array_info function returns information about the array a. It returns three values: an 1-d integer array array specifying the size of each dimension of a, the number of dimensions of a, and the data type of a.

Example

The array_info function may be used to find the number of rows of an array:

    define num_rows (a)
    {
       variable dims, num_dims, data_type;

       (dims, num_dims, data_type) = array_info (a);
       return dims [0];
    }
For 1-d arrays, this information is more easily obtained from the length function.
See Also

typeof, reshape, length, _reshape

1.3 array_map

Synopsis

Apply a function to each element of an array

Usage

Array_Type array_map (type, func, arg0, ...)

    DataType_Type type;
    Ref_Type func;
Description

The array_map function may be used to apply a function to each element of an array and returns the result as an array of a specified type. The type parameter indicates what kind of array should be returned and generally corresponds to the return type of the function. The arg0 parameter should be an array and is used to determine the dimensions of the resulting array. If any subsequent arguments correspond to an array of the same size, then those array elements will be passed in parallel with the first arrays arguments.

Example

The first example illustrates how to apply the strlen function to an array of strings:

     S = ["", "Train", "Subway", "Car"];
     L = array_map (Integer_Type, &strlen, S);
This is equivalent to:
     S = ["", "Train", "Subway", "Car"];
     L = Integer_Type [length (S)];
     for (i = 0; i < length (S); i++) L[i] = strlen (S[i]);

Now consider an example involving the strcat function:

     files = ["slang", "slstring", "slarray"];

     exts = ".c";
     cfiles = array_map (String_Type, &strcat, files, exts);
     % ==> cfiles = ["slang.c slstring.c slarray.c"];

     exts =  [".a",".b",".c"];
     xfiles = array_map (String_Type, &strcat, files, exts);
     % ==> xfiles = ["slang.a", "slstring.b", "slarray.c"];
Notes

Many mathemetical functions already work transparantly on arrays. For example, the following two statements produce identical results:

     B = sin (A);
     B = array_map (Double_Type, &sin, A);
See Also

array_info, strlen, strcat, sin

1.4 array_sort

Synopsis

Sort an array

Usage

Array_Type array_sort (Array_Type a [, String_Type or Ref_Type f])

Description

array_sort sorts the array a into ascending order and returns an integer array that represents the result of the sort. If the optional second parameter f is present, the function specified by f will be used to compare elements of a; otherwise, a built-in sorting function will be used.

If f is present, then it must be either a string representing the name of the comparison function, or a reference to the function. The sort function represented by f must be a S-lang user-defined function that takes two arguments. The function must return an integer that is less than zero if the first parameter is considered to be less than the second, zero if they are equal, and a value greater than zero if the first is greater than the second.

If the comparision function is not specified, then a built-in comparison function appropriate for the data type will be used. For example, if a is an array of character strings, then the sort will be preformed using strcmp.

The integer array returned by this function is simply an index that indicates the order of the sorted array. The input array a is not changed.

Example

An array of strings may be sorted using the strcmp function since it fits the specification for the sorting function described above:

     variable A = String_Type [3];
     A[0] = "gamma"; A[1] = "alpha"; A[2] = "beta";

     variable I = array_sort (A, &strcmp);
Alternatively, one may use
     variable I = array_sort (A);
to use the built-in comparison function.

After the array_sort has executed, the variable I will have the values [2, 0, 1]. This array can be used to re-shuffle the elements of A into the sorted order via the array index expression A = A[I].

See Also

strcmp

1.5 init_char_array

Synopsis

Initialize an array of characters

Usage

init_char_array (Array_Type a, String_Type s)

Description

The init_char_array function may be used to initialize a character array a by setting the elements of the array a to the corresponding characters of the string s.

Example

The statements

     variable a = Char_Type [10];
     init_char_array (a, "HelloWorld");
creates an character array and initializes its elements to the characters in the string "HelloWorld".
Notes

The character array must be large enough to hold all the characters of the initialization string.

See Also

bstring_to_array, strlen, strcat

1.6 length

Synopsis

Get the length of an object

Usage

Integer_Type length (obj)

Description

The length function may be used to get information about the length of an object. For simple scalar data-types, it returns 1. For arrays, it returns the total number of elements of the array.

Notes

If obj is a string, length returns 1 because a String_Type object is considered to be a scalar. To get the number of characters in a string, use the strlen function.

See Also

array_info, typeof, strlen

1.7 reshape

Synopsis

Reshape an array

Usage

reshape (Array_Type A, Array_Type I)

Description

The reshape function changes the size of A to have the size specified by the 1-d integer array I. The elements of I specify the new dimensions of A and must be consistent with the number of elements A.

Example

If A is a 100 element 1-d array, it can be changed to a 2-d 20 by 5 array via

      reshape (A, [20, 5]);
However, reshape(A, [11,5]) will result in an error because the the [11,5] array specifies 55 elements.
Notes

Since reshape modifies the shape of an array, and arrays are treated as references, then all references to the array will reference the new shape. If this effect is unwanted, then use the _reshape function instead.

See Also

_reshape, array_info

1.8 transpose

Synopsis

Transpose a 2d array

Usage

Array_Type transpose (Array_Type a)

Description

The transpose function returns the transpose of a specified array. By definition, the transpose of an array, say one with elements a[i,j,...k] is an array whose elements are a[k,...,j,i].

See Also

_reshape, reshape, array_info

1.9 where

Synopsis

Get indices where an integer array is non-zero

Usage

Array_Type where (Array_Type a)

Description

The where function examines an integer array a and returns a 2-d integer array whose rows are the indices of a where the corresponding element of a is non-zero.

Example

Consider the following:

    variable X = [0.0:10.0:0.01];
    variable A = sin (X);
    variable I = where (A < 0.0);
    A[I] = cos (X) [I];
Here the variable X has been assigned an array of doubles whose elements range from 0.0 through 10.0 in increments of 0.01. The second statement assigns A to an array whose elements are the sin of the elements of X. The third statement uses the where function to get the indices of the elements of A that are less than 0.0. Finally, the last statement substitutes into A the cos of the elements of X at the positions of A where the corresponding sin is less than 0. The end result is that the elements of A are a mixture of sines and cosines.
See Also

array_info, sin, cos

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Associative Array Functions

 Next Previous Contents

2. Associative Array Functions

2.1 assoc_delete_key

Synopsis

Delete a key from an Associative Array

Usage

assoc_delete_key (Assoc_Type a, String_Type k)

Description

The assoc_delete_key function deletes a key given by k from the associative array a. If the specified key does not exist in a, then this function has no effect.

See Also

assoc_key_exists, assoc_get_keys

2.2 assoc_get_keys

Synopsis

Return all the key names of an Associative Array

Usage

String_Type[] assoc_get_keys (Assoc_Type a)

Description

This function returns all the key names of an associative array a as an ordinary one dimensional array of strings. If the associative array contains no keys, an empty array will be returned.

Example

The following function computes the number of keys in an associative array:

      define get_num_elements (a)
      {
         return length (assoc_get_keys (a));
      }
See Also

assoc_get_values, assoc_key_exists, assoc_delete_key, length

2.3 assoc_get_values

Synopsis

Return all the values of an Associative Array

Usage

Array_Type assoc_get_keys (Assoc_Type a)

Description

This function returns all the values in the associative array a as an array of proper type. If the associative array contains no keys, an empty array will be returned.

Example

Suppose that a is an associative array of type Integer_Type, i.e., it was created via

      variable a = Assoc_Type[Integer_Type];
The the following may be used to print the values of the array in ascending order:
      static define int_sort_fun (x, y)
      {
         return sign (x - y);
      }
      define sort_and_print_values (a)
      {
         variable i, v;

         v = assoc_get_values (a);
         i = array_sort (v, &int_sort_fun);
         v = v[i];
         foreach (v)
           {
              variable vi = ();
              () = fprintf (stdout, "%d\n", vi);
           }
      }
See Also

assoc_get_values, assoc_key_exists, assoc_delete_key, array_sort

2.4 assoc_key_exists

Synopsis

Check to see whether a key exists in an Associative Array

Usage

Integer_Type assoc_key_exists (Assoc_Type a, String_Type k)

Description

The assoc_key_exists function may be used to determine whether or not a specified key k exists in an associative array a. It returns 1 if the key exists, or 0 if it does not.

See Also

assoc_get_keys, assoc_get_values, assoc_delete_key

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Functions that Operate on Strings

 Next Previous Contents

3. Functions that Operate on Strings

3.1 Sprintf

Synopsis

Format objects into a string

Usage

String_Type Sprintf (String_Type format, ..., Integer_Type n)

Description

Sprintf formats a string from n objects according to format. Unlike sprintf, the Sprintf function requires the number of items to format.

The format string is a C library sprintf style format descriptor. Briefly, the format string may consist of ordinary characters (not including the % character), which are copied into the output string as-is, and a conversion specification introduced by the % character. The % character must be followed by at least one other character to specify the conversion:

     s    value is a string
     f    value is a floating point number
     e    print float in exponential form, e.g., 2.345e08
     g    print float as e or g, depending upon its value
     c    value is an ascii character
     %    print the percent character
     d    print a signed decimal integer
     u    print an unsigned decimal integer
     o    print an integer as octal
     X    print an integer as hexadecimal
     S    convert value to a string and format as string
Note that %S is a S-lang extension which will cause the value to be formatted as string. In fact, sprintf("%S",x) is equivalent to sprintf("%s",string(x)).
     s = Sprintf("%f is greater than %f but %s is better than %s\n",
                 PI, E, "Cake" "Pie", 4);
The final argument to Sprintf is the number of items to format; in this case, there are 4 items.
See Also

sprintf, string, sscanf

3.2 create_delimited_string

Synopsis

Concatenate strings using a delimiter

Usage

String_Type create_delimited_string (delim, s_1, s_2, ..., s_n, n)

    String_Type delim, s_1, ..., s_n
    Integer_Type n
Description

create_delimited_string performs a concatenation operation on the n strings s_1, ...,s_n, using the string delim as a delimiter. The resulting string is equivalent to one obtained via

      s_1 + delim + s_2 + delim + ... + s_n
Example

One use for this function is to construct path names, e.g.,

    create_delimited_string ("/", "user", "local", "bin", 3);
will produce "usr/local/bin".
Notes

The expression strcat(a,b) is equivalent to create_delimited_string("", a, b, 2).

See Also

strjoin, is_list_element, extract_element, strchop, strcat

3.3 extract_element

Synopsis

Extract the nth element of a string with delimiters

Usage

String_Type extract_element (String_Type list, Integer_Type nth, Integer_Type delim);

Description

The extract_element function may be used to extract the nth element of the delim delimited list of strings list. The function will return the nth element of the list, unless nth specifies more elements than the list contains, in which case NULL will be returned. Elements in the list are numbered from 0.

Example

The expression

     extract_element ("element 0, element 1, element 2", 1, ',')
returns the string " element 1", whereas
     extract_element ("element 0, element 1, element 2", 1, ' ')
returns "0,".

The following function may be used to compute the number of elements in the list:

     define num_elements (list, delim)
     {
        variable nth = 0;
        while (NULL != extract_element (list, nth, delim))
          nth++;
        return nth;
     }

Alternatively, the strchop function may be more useful. In fact, extract_element may be expressed in terms of the function strchop as

    define extract_element (list, nth, delim)
    {
       list = strchop(list, delim, 0);
       if (nth >= length (list))
         return NULL;
       else
         return list[nth];
    }
and the num_elements function used above may be recoded more simply as:
    define num_elements (list, delim)
    {
       return length (strchop (length, delim, 0));
    }
See Also

is_list_element, is_substr, strtok, strchop, create_delimited_string

3.4 is_list_element

Synopsis

Test whether a delimited string contains a specific element

Usage

Integer_Type is_list_element (String_Type list, String_Type elem, Integer_Type delim)

Description

The is_list_element function may be used to determine whether or not a delimited list of strings, list, contains the element elem. If elem is not an element of list, the function will return zero, otherwise, it returns 1 plus the matching element number.

Example

The expression

     is_list_element ("element 0, element 1, element 2", "0,", ' ');
returns 2 since "0," is element number one of the list (numbered from zero).
See Also

extract_element, is_substr, create_delimited_string

3.5 is_substr

Synopsis

Test for a specified substring within a string.

Usage

Integer_Type is_substr (String_Type a, String_Type b)

Description

This function may be used to determine if a contains the string b. If it does not, the function returns 0; otherwise it returns the position of the first occurance of b in a.

Notes

It is important to remember that the first character of a string corresponds to a position value of 1.

See Also

substr, string_match, str_replace

3.6 make_printable_string

Synopsis

Format a string suitable for parsing

Usage

String_Type make_printable_string(String_Type str)

Description

This function formats a string in such a way that it may be used as an argument to the eval function. The resulting string is identical to str except that it is enclosed in double quotes and the backslash, newline, and double quote characters are expanded.

See Also

eval, str_quote_string

3.7 sprintf

Synopsis

Format objects into a string

Usage

String sprintf (String format, ...);

Description

This function performs a similar task as the C function with the same name. It differs from the S-lang function Sprintf in that it does not require the number of items to format. See the documentation for Sprintf for more information.

See Also

Sprintf, string, sscanf, vmessage

3.8 sscanf

Synopsis

Parse a formatted string

Usage

Int_Type sscanf (s, fmt, r1, ... rN)

    String_Type s, fmt;
    Ref_Type r1, ..., rN
Description

The sscanf function parses the string s according to the format fmt and sets the variables whose references are given by r1, ..., rN. The function returns the number of references assigned, or -1 upon error.

The format string fmt consists of ordinary characters and conversion specifiers. A conversion specifier begins with the special character % and is described more fully below. A white space character in the format string matches any amount of whitespace in the input string. Parsing of the format string stops whenever a match fails.

The % is used to denote a conversion specifier whose general form is given by %[*][width][type]format where the brackets indicate optional items. If * is present, then the conversion will be performed by no assignment to a reference will be made. The width specifier specifies the maximum field width to use for the conversion. The type modifier is used to indicate size of the object, e.g., a short integer, as follows.

If type is given as the character h, then if the format conversion is for an integer (dioux), the object assigned will be a short integer. If type is l, then the conversion will be to a long integer for integer conversions, or to a double precession floating point number for floating point conversions.

The format specifier is a character that specifies the conversion:

       %     Matches a literal percent character.  No assigment is
             performed.
       d     Matches a signed decimal integer.
       D     Matches a long decimal integer (equiv to `ld')
       u     Matches an unsigned decimal integer
       U     Matches an unsigned long decimal integer (equiv to `lu')
       i     Matches either a hexidecimal integer, decimal integer, or
             octal integer.
       I     Equivalent to `li'.
       x     Matches a hexidecimal integer.
       X     Matches a long hexidecimal integer (same as `lx').
       e,f,g Matches a decimal floating point number (Float_Type).
       E,F,G Matches a double precision floating point number, same as `lf'.
       s     Matches a string of non-whitespace characters (String_Type).
       c     Matches one character.  If width is given, width
             characters are matched.
       n     Assigns the number of characters scanned so far.
       [...] Matches zero or more characters from the set of characters
             enclosed by the square brackets.  If '^' is given as the
             first character, then the complement set is matched.
Example

Suppose that s is "Coffee: (3,4,12.4)". Then

    n = sscanf (s, "%[a-zA-Z]: (%d,%d,%lf)", &item, &x, &y, &z);
will set n to 4, item to "Coffee", x to 3, y to 4, and z to the double precision number 12.4. However,
    n = sscanf (s, "%s: (%d,%d,%lf)", &item, &x, &y, &z);
will set n to 1, item to "Coffee:" and the remaining variables will not be assigned.
See Also

sprintf, unpack, string, atof, int, integer, string_match

3.9 str_delete_chars

Synopsis

Delete characters from a string

Usage

String_Type str_delete_chars (String_Type str, String_Type del_set

Description

This function may be used to delete the set of characters specified by del_set from the string str. The result is returned.

Example

    str = str_delete_chars (str, "^A-Za-z");
will remove all characters except A-Z and a-z from str.

3.10 str_quote_string

Synopsis

Escape characters in a string.

Usage

String_Type str_quote_string(String_Type str, String_Type qlis, Integer_Type quote)

Description

The str_quote_string returns a string identical to str except that all characters in the set specified by the string qlis are escaped with the quote character, including the quote character itself. This function is useful for making a string that can be used in a regular expression.

Example

Execution of the statements

   node = "Is it [the coat] really worth $100?";
   tag = str_quote_string (node, "\\^$[]*.+?", '\\');
will result in tag having the value:
    Is it \[the coat\] really worth \$100\?
See Also

str_uncomment_string, make_printable_string

3.11 str_replace

Synopsis

Replace a substring of a string

Usage

Integer_Type str_replace (String_Type a, String_Type b, String_Type c)

Description

The str_replace function replaces the first occurance of b in a with c and returns an integer that indicates whether a replacement was made or not. If b does not occur in a, zero is returned. However, if b occurs in a, a non-zero integer is returned as well as the new string resulting from the replacement.

Example

  define str_replace_all (orig, match, replacement)
  {
     while (str_replace (orig, match, replacement))
        orig = ();
     return orig;
  }
is a function that replaces all occurances in a string.
See Also

is_substr, strsub, strtrim, strtrans, str_delete_chars

3.12 str_uncomment_string

Synopsis

Remove comments from a string

Usage

String_Type str_uncomment_string(String_Type s, String_Type beg, String_Type end)

Description

This function may be used to remove comments from a string s. The parameters, beg and end, are strings of equal length whose corresponding characters specify the begin and end comment characters, respectively. It returns the uncommented string.

Example

The expression

     str_uncomment_string ("Hello (testing) 'example' World", "'(", "')")
returns the string "Hello World".
Notes

This routine does not handle multicharacter comment delimiters and it assumes that comments are not nested.

See Also

str_quote_string

3.13 strcat

Synopsis

Concatenate two strings

Usage

String_Type strcat (String_Type a, String_Type b)

Description

The strcat function takes two string valued quantities, a and b, concatenates them together and returns the result.

Example

    strcat ("Hello ", "World");
produces the string "Hello World".
Notes

This function is equivalent to the binary operation a+b.

See Also

sprintf, create_delimited_string

3.14 strchop

Synopsis

Chop or split a string into substrings.

Usage

String_Type[] strchop (String_Type str, Integer_Type delim, Integer_Type quote)

Description

The strchop function may be used to split-up a string str that consists of substrings delimited by the character specified by delim. If the integer quote is non-zero, it will be taken as a quote character for the delimiter. The function returns the substrings as an array.

Example

The following function illustrates how to sort a comma separated list of strings:

     define sort_string_list (a)
     {
        variable i, b, c;
        b = strchop (a, ',', 0);

        i = array_sort (b, &strcmp);
        b = b[i];   % rearrange

        % Convert array back into comma separated form
        return strjoin (b, ",");
     }
Notes

The semantics of this strchop and strchopr have been changed since version 1.2.x of the interpreter. Old versions of these functions returned the values on the stack, which meant that one could not chop up arbitrarily long strings that consist of many substrings.

The function strchopr should be used if it is desired to have the string chopped-up in the reverse order.

See Also

strchopr, extract_element, strjoin, strtok

3.15 strchopr

Synopsis

Chop or split a string into substrings.

Usage

String_Type[] strchopr (String_Type str, String_Type delim, String_Type quote)

Description

This routine performs exactly the same function as strchop except that it returns the substrings in the reverse order. See the documentation for strchop for more information.

See Also

strchop, extract_element, strtok, strjoin

3.16 strcmp

Synopsis

Compare two strings

Usage

Interpret strcmp (String_Type a, String_Type b)

Description

The strcmp function may be used to perform a case-sensitive string comparison, in the lexicongraphic sense, on strings a and b. It returns 0 if the strings are identical, a negative integer if a is less than b, or a positive integer if a is greater than b.

Example

The strup function may be used to perform a case-insensitive string comparison:

    define case_insensitive_strcmp (a, b)
    {
      return strcmp (strup(a), strup(b));
    }
Notes

One may also use one of the binary comparison operators, e.g., a > b.

See Also

strup, strncmp

3.17 strcompress

Synopsis

Remove excess whitespace characters from a string

Usage

String_Type strcompress (String_Type s, String_Type white)

Description

The strcompress function compresses the string s by replacing a sequence of one or more characters from the set white by the first character of white. In addition, it also removes all leading and trailing characters from s that are part of white.

Example

The expression

    strcompress (",;apple,,cherry;,banana", ",;");
returns the string "apple,cherry,banana".
See Also

strtrim, strtrans

3.18 string_match

Synopsis

Match a string against a regular expression

Usage

Integer_Type string_match(String_Type str, String_Type pat, Integer_Type pos)

Description

The string_match function returns zero if str does not match regular expression specified by pat. This function performs the match starting at position pos (numbered from 1) in str. This function returns the position of the start of the match. To find the exact substring actually matched, use string_match_nth.

See Also

string_match_nth, strcmp, strncmp

3.19 string_match_nth

Synopsis

Get the result of the last call to string_match

Usage

(Integer_Type, Integer_Type) = string_match_nth(Integer_Type nth)

Description

The string_match_nth function returns two integers describing the result of the last call to string_match. It returns both the offset into the string and the length of characters matches by the nth submatch.

By convention, nth equal to zero means the entire match. Otherwise, nth must be an integer with a value 1 through 9, and refers to the set of characters matched by the nth regular expression enclosed by the pairs \(, \).

Example

Consider:

     variable matched, pos, len;
     matched = string_match("hello world", "\\([a-z]+\\) \\([a-z]+\\)", 1);
     if (matched) (pos, len) = string_match_nth(2);
This will set matched to 1 since a match will be found at the first position, pos to 6 since w is offset 6 characters from the beginning of the string, and len to 5 since "world" is 5 characters long.
Notes

The position offset is not affected by the value of the offset parameter to the string_match function. For example, if the value of the last parameter to the string_match function had been 3, pos would still have been set to 6.

Note also that string_match_nth returns the offset from the beginning of the string and not the position of the match.

See Also

string_match

3.20 strjoin

Synopsis

Concatenate elements of a string array

Usage

String_Type strjoin (Array_Type a, String_Type delim)

Description

The strjoin function operates on an array of strings by joining successive elements together separated with a delimiter delim. If delim is the empty string "", then the result will simply be the concatenation of the elements.

Example

Suppose that

      days = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun"];
Then strjoin (days,"+") will produce "Sun+Mon+Tue+Wed+Thu+Fri+Sat+Sun". Similarly, strjoin (["","",""], "X") will produce "XX".
See Also

create_delimited_string, strchop, strcat

3.21 strlen

Synopsis

Compute the length of a string

Usage

Integer_Type strlen (String_Type a)

Description

The strlen function may be used to compute the length of a string.

Example

After execution of

   variable len = strlen ("hello");
len will have a value of 5.
See Also

bstrlen, length, substr

3.22 strlow

Synopsis

Convert a string to lowercase

Usage

String_Type strlow (String_Type s)

Description

The strlow function takes a string s and returns another string identical to s except that all upper case characters that comprise s will be converted to lower case.

Example

The function

    define Strcmp (a, b)
    {
      return strcmp (strlow (a), strlow (b));
    }
performs a case-insensitive comparison operation of two strings by converting them to lower case first.
See Also

strup, tolower, strcmp, strtrim, define_case

3.23 strncmp

Synopsis

Compare the first few characters of two strings

Usage

Integer_Type strncmp (String_Type a, String_Type b, Integer_Type n)

Description

This function behaves like strcmp except that it compares only the first n characters in the strings a and b. See the documentation for strcmp for information about the return value.

Example

The expression

     strcmp ("apple", "appliance", 3);
will return zero since the first three characters match.
See Also

strcmp, strlen

3.24 strsub

Synopsis

Replace a character with another in a string.

Usage

String_Type strsub (String_Type s, Integer_Type pos, Integer_Type ch)

Description

The strsub character may be used to substitute the character ch for the character at position pos of the string s. The resulting string is returned.

Example

    define replace_spaces_with_comma (s)
    {
      variable n;
      while (n = is_substr (s, " "), n) s = strsub (s, n, ',');
      return s;
    }
For uses such as this, the strtrans function is a better choice.
Notes

The first character in the string s is specified by pos equal to 1.

See Also

is_substr, str_replace, strlen

3.25 strtok

Synopsis

Extract tokens from a string

Usage

String_Type[] strtok (String_Type str [,String_Type white])

Description

strtok breaks the string str into a series of tokens and returns them as an array of strings. If the second parameter white is present, then it specifies the set of characters that are to be regarded as whitespace when extracting the tokens, and may consist of the whitespace characters or a range of such characters. If the first character of white is '^', then the whitespace characters consist of all characters except those in white. For example, if white is " \t\n,;.", then those characters specifiy the whitespace characters. However, if white is given by "^a-zA-Z0-9_", then any character is a whitespace character except those in the ranges a-z, A-Z, 0-9, and the underscore character.

If the second parameter is not present, then it defaults to " \t\r\n\f".

Example

The following example may be used to count the words in a text file:

    define count_words (file)
    {
       variable fp, line, count;

       fp = fopen (file, "r");
       if (fp == NULL) return -1;

       count = 0;
       while (-1 != fgets (&line, fp))
         {
           line = strtok (line, "^a-zA-Z");
           count += length (line);
         }
       () = fclose (fp);
       return count;
    }
See Also

strchop, strcompress, extract_element, strjoin

3.26 strtrans

Synopsis

Replace characters in a string

Usage

String_Type strtrans (str, old_set, new_set)

   String_Type str, old_set, new_set;
Description

The strtrans function may be used to replace all the characters from the set old_set with the corresponding characters from new_set in the string str. If new_set is empty, then the characters in new_set will be removed from str. This function returns the result.

Example

    str = strtrans (str, "A-Z", "a-z");   % lower-case str
    str = strtrans (str, "^0-9", " ");    % Replace anything but 0-9 by space
See Also

str_replace, strtrim, strup, strlow

3.27 strtrim

Synopsis

Remove whitespace from the ends of a string

Usage

String_Type strtrim (String_Type s [,String_Type w])

Description

The strtrim function removes all leading and trailing whitespace characters from the string s and returns the result. The optional second parameter specifies the set of whitespace characters. If the argument is not present, then the set defaults to " \t\r\n".

See Also

strtrim_beg, strtrim_end, strcompress

3.28 strtrim_beg

Synopsis

Remove leading whitespace from a string

Usage

String_Type strtrim_beg (String_Type s [,String_Type w])

Description

The strtrim_beg function removes all leading whitespace characters from the string s and returns the result. The optional second parameter specifies the set of whitespace characters. If the argument is not present, then the set defaults to " \t\r\n".

See Also

strtrim, strtrim_end, strcompress

3.29 strtrim_end

Synopsis

Remove trailing whitespace from a string

Usage

String_Type strtrim_end (String_Type s [,String_Type w])

Description

The strtrim_end function removes all trailing whitespace characters from the string s and returns the result. The optional second parameter specifies the set of whitespace characters. If the argument is not present, then the set defaults to " \t\r\n".

See Also

strtrim, strtrim_beg, strcompress

3.30 strup

Synopsis

Convert a string to uppercase

Usage

String_Type strup (String_Type s)

Description

The strup function takes a string s and returns another string identical to s except that all lower case characters that comprise s will be converted to upper case.

Example

The function

    define Strcmp (a, b)
    {
      return strcmp (strup (a), strup (b));
    }
performs a case-insensitive comparison operation of two strings by converting them to upper case first.
See Also

strlow, toupper, strcmp, strtrim, define_case, strtrans

3.31 substr

Synopsis

Extract a substring from a string

Usage

String_Type substr (String_Type s, Integer_Type n, Integer_Type len)

Description

The substr function returns a substring with length len of the string s beginning at position n. If len is -1, the entire length of the string s will be used for len. The first character of s is given by n equal to 1.

Example

     substr ("To be or not to be", 7, 5);
returns "or no"
Notes

In many cases it is more convenient to use array indexing rather than the substr function. In fact, substr(s,i+1,strlen(s)) is equivalent to s[[i:]].

See Also

is_substr, strlen

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Functions that Manipulate Structures

 Next Previous Contents

4. Functions that Manipulate Structures

4.1 _push_struct_field_values

Synopsis

Push the values of a structure's fields onto the stack

Usage

Integer_Type num = _push_struct_field_values (Struct_Type s)

Description

The _push_struct_field_values function pushes the values of all the fields of a structure onto the stack, returning the number of items pushed. The fields are pushed such that the last field of the structure is pushed first.

See Also

get_struct_field_names, get_struct_field

4.2 get_struct_field

Synopsis

Get the value associated with a structure field

Usage

x = get_struct_field (Struct_Type s, String field_name)

Description

The get_struct_field function gets the value of the field whose name is specified by field_name of the structure s.

Example

The following example illustrates how this function may be used to to print the value of a structure.

      define print_struct (s)
      {
         variable name;

         foreach (get_struct_field_names (s))
           {
             name = ();
             value = get_struct_field (s, name);
             vmessage ("s.%s = %s\n", name, string(value));
           }
      }
See Also

set_struct_field, get_struct_field_names, array_info

4.3 get_struct_field_names

Synopsis

Retrieve the field names associated with a structure

Usage

String_Type[] = get_struct_field_names (Struct_Type s)

Description

The get_struct_field_names function returns an array of strings whose elements specify the names of the fields of the struct s.

Example

The following example illustrates how the get_struct_field_names function may be used to print the value of a structure.

      define print_struct (s)
      {
         variable name, value;

         foreach (get_struct_field_names (s))
           {
             name = ();
             value = get_struct_field (name);
             vmessage ("s.%s = %s\n", name, string (value));
           }
      }
See Also

_push_struct_field_values, get_struct_field

4.4 set_struct_field

Synopsis

Set the value associated with a structure field

Usage

set_struct_field (s, field_name, field_value)

   Struct_Type s;
   String_Type field_name;
   Generic_Type field_value;
Description

The set_struct_field function sets the value of the field whose name is specified by field_name of the structure s to field_value.

See Also

get_struct_field, get_struct_field_names, set_struct_fields, array_info

4.5 set_struct_fields

Synopsis

Set the fields of a structure

Usage

set_struct_fields (Struct_Type s, ...)

Description

The set_struct_fields function may be used to set zero or more fields of a structure. The fields are set in the order in which they were created when the structure was defined.

Example

    variable s = struct { "name", "age", "height" };
    set_struct_fields (s, "Bill", 13, 64);
See Also

set_struct_field, get_struct_field_names

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Informational Functions

 Next Previous Contents

5. Informational Functions

5.1 _NARGS

Synopsis

The number of parameters passed to a function

Usage

Integer_Type _NARGS The value of the _NARGS variable represents the number of arguments passed to the function. This variable is local to each function.

Example

This example uses the _NARGS variable to print the list of values passed to the function:

     define print_values ()
     {
        variable arg;

        if (_NARGS == 0)
          {
             message ("Nothing to print");
             return;
          }
        foreach (__pop_args (_NARGS))
          {
             arg = ();
             vmessage ("Argument value is: %S", arg.value);
          }
     }
See Also

__pop_args, __push_args, typeof

5.2 __get_defined_symbols

Synopsis

Get the symbols defined by the preprocessor

Usage

Integer_Type __get_defined_symbols ()

Description

The __get_defined_symbols functions is used to get the list of all the symbols defined by the S-lang preprocessor. It pushes each of the symbols on the stack followed by the number of items pushed.

See Also

is_defined, _apropos

5.3 __is_initialized

Synopsis

Determine whether or not a variable has a value

Usage

Integer_Type __is_initialized (Ref_Type r)

Description

This function returns non-zero of the object referenced by r is initialized, i.e., whether it has a value. It returns 0 if the referenced object has not been initialized.

Example

For example, the function:

    define zero ()
    {
       variable f;
       return __is_initialized (&f);
    }
will always return zero, but
    define one ()
    {
       variable f = 0;
       return __is_initialized (&f);
    }
will return one.
Notes

It is easy to see why a reference to the variable must be passed to __is_initialized and not the variable itself; otherwise, the value of the variable would be passed and the variable may have no value if it was not initialized.

See Also

__get_reference, __uninitialize, is_defined, typeof, eval

5.4 _apropos

Synopsis

Generate a list of functions and variables

Usage

Array_Type _apropos (String_Type ns, String_Type s, Integer_Type flags)

Description

The _apropos function may be used to get a list of all defined objects in the namespace ns whose name matches the regular expression s and whose type matches those specified by flags. It returns an array of strings representing the matches.

The second parameter flags is a bit mapped value whose bits are defined according to the following table

     1          Intrinsic Function
     2          User-defined Function
     4          Intrinsic Variable
     8          User-defined Variable
Example

    define apropos (s)
    {
      variable n, name, a;
      a = _apropos ("Global", s, 0xF);

      vmessage ("Found %d matches:", length (a));
      foreach (a)
        {
           name = ();
           message (name);
        }
    }
prints a list of all matches.
Notes

If the namespace specifier ns is the empty string "", then the namespace will default to the static namespace of the current compilation unit.

See Also

is_defined, sprintf

5.5 _function_name

Synopsis

Returns the name of the currently executing function

Usage

String _function_name ();

Description

This function returns the name of the currently executing function. If called from top-level, it returns the empty string.

See Also

_trace_function, is_defined

5.6 _slang_version

Synopsis

The S-Lang library version number

Usage

Integer_Type _slang_version

Description

The _slang_version variable is read-only and and whose value represents the number of the S-lang library.

See Also

_slang_version_string

5.7 _slang_version_string

Synopsis

The S-Lang library version number as a string

Usage

String_Type _slang_version_string

Description

The _slang_version_string variable is read-only and whose value represents the version number of the S-lang library.

See Also

_slang_version

5.8 get_doc_string_from_file

Synopsis

Read documentation from a file

Usage

String_Type get_doc_string_from_file (String_Type f, String_Type t)

Description

get_doc_string_from_file opens the documentation file f and searches it for topic t. It returns the documentation for t upon success, otherwise it returns NULL upon error. It will fail if f could not be opened or does not contain documentation for the topic.

See Also

stat_file

5.9 is_defined

Synopsis

Indicate whether a variable or function defined.

Usage

Integer_Type is_defined (String_Type obj)

Description

This function is used to determine whether or not a function or variable whose name is obj has been defined. If obj is not defined, the function returns 0. Otherwise, it returns a non-zero value that defpends on the type of object obj represents. Specifically, it returns one of the following values:

     +1 if an intrinsic function
     +2 if user defined function
     -1 if intrinsic variable
     -2 if user defined variable
      0 if undefined
Example

For example, consider the function:

    define runhooks (hook)
    {
       if (2 == is_defined(hook)) eval(hook);
    }
This function could be called from another S-lang function to allow customization of that function, e.g., if the function represents a mode, the hook could be called to setup keybindings for the mode.
See Also

typeof, eval, autoload, __get_reference, __is_initialized

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Mathematical Functions

 Next Previous Contents

6. Mathematical Functions

6.1 Conj

Synopsis

Compute the complex conjugate of a number

Usage

z1 = Conj (z)

Description

The Conj function returns the complex conjugate of a number. If its argument is an array, the Conj function will be applied to each element and the result returned as an array.

See Also

Real, Imag, abs

6.2 Imag

Synopsis

Compute the imaginary part of a number

Usage

i = Imag (z)

Description

The Imag function returns the imaginary part of a number. If its argument is an array, the Imag function will be applied to each element and the result returned as an array.

See Also

Real, Conj, abs

6.3 Real

Synopsis

Compute the real part of a number

Usage

r = Real (z)

Description

The Real function returns the real part of a number. If its argument is an array, the Real function will be applied to each element and the result returned as an array.

See Also

Imag, Conj, abs

6.4 abs

Synopsis

Compute the absolute value of a number

Usage

y = abs(x)

Description

The abs function returns the absolute value of an arithmetic type. If its argument is a complex number (Complex_Type), then it returns the modulus. If the argument is an array, a new array will be created whose elements are obtained from the original array by using the abs function.

See Also

sign, sqr

6.5 acos

Synopsis

Compute the arc-cosine of an number

Usage

y = acos (x)

Description

The acos function computes the arc-cosine of a number and returns the result as an array. If its argument is an array, the acos function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.6 acosh

Synopsis

Compute the inverse cosh of an number

Usage

y = acosh (x)

Description

The acosh function computes the inverse cosh of a number and returns the result as an array. If its argument is an array, the acosh function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.7 asin

Synopsis

Compute the arc-sine of an number

Usage

y = asin (x)

Description

The asin function computes the arc-sine of a number and returns the result as an array. If its argument is an array, the asin function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.8 asinh

Synopsis

Compute the inverse-sinh of an number

Usage

y = asinh (x)

Description

The asinh function computes the inverse-sinh of a number and returns the result as an array. If its argument is an array, the asinh function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.9 atan

Synopsis

Compute the arc-tangent of an number

Usage

y = atan (x)

Description

The atan function computes the arc-tangent of a number and returns the result as an array. If its argument is an array, the atan function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.10 atanh

Synopsis

Compute the inverse-tanh of an number

Usage

y = atanh (x)

Description

The atanh function computes the inverse-tanh of a number and returns the result as an array. If its argument is an array, the atanh function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.11 cos

Synopsis

Compute the cosine of an number

Usage

y = cos (x)

Description

The cos function computes the cosine of a number and returns the result as an array. If its argument is an array, the cos function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.12 cosh

Synopsis

Compute the hyperbolic cosine of an number

Usage

y = cosh (x)

Description

The cosh function computes the hyperbolic cosine of a number and returns the result as an array. If its argument is an array, the cosh function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.13 exp

Synopsis

Compute the exponential of an number

Usage

y = exp (x)

Description

The exp function computes the exponential of a number and returns the result as an array. If its argument is an array, the exp function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.14 log

Synopsis

Compute the logarithm of an number

Usage

y = log (x)

Description

The log function computes the logarithm of a number and returns the result as an array. If its argument is an array, the log function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.15 log10

Synopsis

Compute the base-10 logarithm of an number

Usage

y = log10 (x)

Description

The log10 function computes the base-10 logarithm of a number and returns the result as an array. If its argument is an array, the log10 function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.16 mul2

Synopsis

Multiply a number by 2

Usage

y = mul2(x)

Description

The mul2 function multiplies an arithmetic type by two and returns the result. If its argument is an array, a new array will be created whose elements are obtained from the original array by using the mul2 function.

See Also

sqr, abs

6.17 polynom

Synopsis

Evaluate a polynomial

Usage

Double_Type polynom(Double_Type a, b, ...c, Integer_Type n, Double_Type x)

Description

The polynom function returns the value of the polynomial expression:

     ax^n + bx^(n - 1) + ... c
Notes

The polynom function should be extended to work with complex and array data types. The current implementation is limited to Double_Type quantities.

See Also

exp

6.18 set_float_format

Synopsis

Set the format for printing floating point values.

Usage

set_float_format (String_Type fmt)

Description

The set_float_format function is used to set the floating point format to be used when floating point numbers are printed. The routines that use this are the traceback routines and the string function. The default value is "%f"

Example

     s = string (PI);                %  --> s = "3.14159"
     set_float_format ("%16.10f");
     s = string (PI);                %  --> s = "3.1415926536"
     set_float_format ("%10.6e");
     s = string (PI);                %  --> s = "3.141593e+00"
See Also

string, sprintf, double

6.19 sign

Synopsis

Compute the sign of a number

Usage

y = sign(x)

Description

The sign function returns the sign of an arithmetic type. If its argument is a complex number (Complex_Type), it returns the sign of the imaginary part of the number. If the argument is an array, a new array will be created whose elements are obtained from the original array by using the sign function.

When applied to a real number or an integer, the sign function returns -1, 0, or +1 according to whether the number is less than zero, equal to zero, or greater than zero, respectively.

See Also

abs

6.20 sin

Synopsis

Compute the sine of an number

Usage

y = sin (x)

Description

The sin function computes the sine of a number and returns the result as an array. If its argument is an array, the sin function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.21 sinh

Synopsis

Compute the hyperbolic sine of an number

Usage

y = sinh (x)

Description

The sinh function computes the hyperbolic sine of a number and returns the result as an array. If its argument is an array, the sinh function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.22 sqr

Synopsis

Compute the square of a number

Usage

y = sqr(x)

Description

The sqr function returns the square of an arithmetic type. If its argument is a complex number (Complex_Type), then it returns the square of the modulus. If the argument is an array, a new array will be created whose elements are obtained from the original array by using the sqr function.

See Also

abs, mul2

6.23 sqrt

Synopsis

Compute the square root of an number

Usage

y = sqrt (x)

Description

The sqrt function computes the square root of a number and returns the result as an array. If its argument is an array, the sqrt function will be applied to each element and the result returned as an array.

See Also

sqr, cos, atan, acosh, cosh

6.24 tan

Synopsis

Compute the tangent of an number

Usage

y = tan (x)

Description

The tan function computes the tangent of a number and returns the result as an array. If its argument is an array, the tan function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

6.25 tanh

Synopsis

Compute the hyperbolic tangent of an number

Usage

y = tanh (x)

Description

The tanh function computes the hyperbolic tangent of a number and returns the result as an array. If its argument is an array, the tanh function will be applied to each element and the result returned as an array.

See Also

cos, atan, acosh, cosh

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Message and Error Functions

 Next Previous Contents

7. Message and Error Functions

7.1 error

Synopsis

Generate an error condition

Usage

error (String_Type msg

Description

The error function generates a S-lang error condition causing the interpreter to start unwinding to top-level. It takes a single string parameter which is displayed on the stderr output device. The error condition may be cleared via an ERROR_BLOCK with the _clear_error function. Consult A Guide to the S-Lang Language for more information.

Example

    define add_txt_extension (file)
    {
       if (typeof (file) != String_Type)
         error ("add_extension: parameter must be a string");
       file += ".txt";
       return file;
    }
See Also

verror, _clear_error, message

7.2 message

Synopsis

Print a string onto the message device

Usage

message (String_Type s

Description

The message function will print the string specified by s onto the message device.

Example

     define print_current_time ()
     {
       message (time ());
     }
Notes

The message device will depend upon the application. For example, the output message device for the jed editor correspond to the line at the bottom of the display window. The default message device is the standard output device.

See Also

vmessage, sprintf, error

7.3 usage

Synopsis

Generate a usage error

Usage

usage (String_Type msg)

Description

The usage function generates a usage exception and displays msg to the message device.

Example

Suppose that some function plot plots an array of x and y values. The such a function could be written to issue a usage message if the wrong number of arguments were passed:

    define plot ()
    {
       variable x, y;

       if (_NARGS != 2)
         usage ("plot (x, y)");

       (x, y) = ();
       % Now do the hard part
          .
          .
    }
See Also

error, message

7.4 verror

Synopsis

Generate an error condition

Usage

verror (String_Type fmt, ...)

Description

The verror function performs the same role as the error function. The only difference is that instead of a single string argument, verror takes a sprintf style argument list.

Example

    define open_file (file)
    {
       variable fp;

       fp = fopen (file, "r");
       if (fp == NULL) verror ("Unable to open %s", file);
       return fp;
    }
Notes

In the current implementation, strictly speaking, the verror function is not an intrinsic function. Rather it is a predefined S-lang function using a combination of Sprintf and error.

See Also

error, Sprintf, vmessage

7.5 vmessage

Synopsis

Print a formatted string onto the message device

Usage

vmessage (String_Type fmt, ...)

Description

The vmessage function formats a sprintf style argument list and displays the resulting string onto the message device.

Notes

In the current implementation, strictly speaking, the vmessage function is not an intrinsic function. Rather it is a predefined S-lang function using a combination of Sprintf and message.

See Also

message, Sprintf, verror

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Time and Date Functions

 Next Previous Contents

8. Time and Date Functions

8.1 _time

Synopsis

Get the current time in seconds

Usage

ULong_Type _time ()

Description

The _time function returns the number of elapsed seconds since 00:00:00 GMT, January 1, 1970. The ctime function may be used to convert this into a string representation.

See Also

ctime, time, localtime, gmtime

8.2 ctime

Synopsis

Convert a calendar time to a string

Usage

String_Type ctime(ULong_Type secs)

Description

This function returns a string representation of the time as given by secs seconds since 1970.

See Also

time, _time, localtime, gmtime

8.3 gmtime

Synopsis

Break down a time in seconds to GMT timezone

Usage

Struct_Type gmtime (Long_Type secs)

Description

The gmtime function is exactly like localtime except that the values in the structure it returns are with respect to GMT instead of the local timezone. See the documentation for localtime for more information.

Notes

On systems that do not support the gmtime C library function, this function is the same as localtime.

See Also

localtime, _time

8.4 localtime

Synopsis

Break down a time in seconds to local timezone

Usage

Struct_Type localtime (Long_Type secs)

Description

The localtime function takes a parameter secs representing the number of seconds since 00:00:00, January 1 1970 UTC and returns a structure containing information about secs in the local timezone. The structure contains the following Int_Type fields:

tm_sec The number of seconds after the minute, normally in the range 0 to 59, but can be up to 61 to allow for leap seconds.

tm_min The number of minutes after the hour, in the range 0 to 59.

tm_hour The number of hours past midnight, in the range 0 to 23.

tm_mday The day of the month, in the range 1 to 31.

tm_mon The number of months since January, in the range 0 to 11.

tm_year The number of years since 1900.

tm_wday The number of days since Sunday, in the range 0 to 6.

tm_yday The number of days since January 1, in the range 0 to 365.

tm_isdst A flag that indicates whether daylight saving time is in effect at the time described. The value is positive if daylight saving time is in effect, zero if it is not, and negative if the information is not available.

See Also

gmtime, _time, ctime

8.5 tic

Synopsis

Start timing

Usage

void tic ()

Description

The tic function restarts the internal clock used for timing the execution of commands. To get the elapsed time of the clock, use the toc function.

See Also

toc, times

8.6 time

Synopsis

Return the current data and time as a string

Usage

String_Type time ()

Description

This function returns the current time as a string of the form:

    Sun Apr 21 13:34:17 1996
See Also

ctime, message, substr

8.7 times

Synopsis

Get process times

Usage

Struct_Type times ()

Description

The times function returns a structure containing the following fields:

    tms_utime     (user time)
    tms_stime     (system time)
    tms_cutime    (user time of child processes)
    tms_cstime    (system time of child processes)
Notes

Not all systems support this function.

See Also

tic, toc, _times

8.8 toc

Synopsis

Get elapsed CPU time

Usage

Double_Type toc ()

Description

The toc function returns the elapsed CPU time in seconds since the last call to tic. The CPU time is the amount of time the CPU spent running the code of the current process.

Example

The tic and toc functions are ideal for timing the execution of the interpreter:

     variable a = "hello", b = "world", c, n = 100000, t;

     tic ();  loop (n) c = a + b; t = toc ();
     vmessage ("a+b took %f seconds\n", t);
     tic ();  loop (n) c = strcat(a,b); t = toc ();
     vmessage ("strcat took %f seconds\n", t);
Notes

This function may not be available on all systems.

The implementation of this function is based upon the times system call. The precision of the clock is system dependent.

See Also

tic, times, _time

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Data-Type Conversion Functions

 Next Previous Contents

9. Data-Type Conversion Functions

9.1 _slang_guess_type

Synopsis

Guess the data type that a string represents.

Usage

DataType_Type _slang_guess_type (String_Type s)

Description

This function tries to determine whether its argument s represents an integer or a floating point number. If it appears to be neither, then a string is assumed. It returns one of three values depending on the format of the string s:

    Integer_Type     :   If it appears to be an integer
    Double_Type      :   If it appears to be a double
    String_Type      :   Anything else.
For example, _slang_guess_type("1e2") returns Double_Type but _slang_guess_type("e12") returns String_Type.
See Also

integer, string, double

9.2 _typeof

Synopsis

Get the data type of an object

Usage

DataType_Type _typeof (x)

Description

This function is similar to the typeof function except in the case of arrays. If the object x is an array, then the data type of the array will be returned. otherwise _typeof returns the data type of x.

Example

  if (Integer_Type == _typeof (x))
    message ("x is an integer or an integer array");
See Also

typeof, array_info, _slang_guess_type, typecast

9.3 atof

Synopsis

Convert a string to a double precision number

Usage

Double_Type atof (String_Type s)

Description

This function converts a string s to a double precision value and returns the result. It performs no error checking on the format of the string. The function _slang_guess_type may be used to check the syntax of the string.

Example

     define error_checked_atof (s)
     {
        switch (_slang_guess_type (s))
        {
           case Double_Type:
             return atof (s);
        }
        {
           case Integer_Type:
             return double (integer (s));
        }

        verror ("%s is is not a double", s);
    }
See Also

typecast, double, _slang_guess_type

9.4 char

Synopsis

Convert an ascii value into a string

Usage

String_Type char (Integer_Type c)

Description

The char function converts an integer ascii value c to a string of unit length such that the first character of the string is c. For example, char('a') returns the string "a".

See Also

integer, string, typedef

9.5 define_case

Synopsis

Define upper-lower case conversion.

Usage

define_case (Integer_Type ch_up, Integer_Type ch_low);

Description

This function defines an upper and lowercase relationship between two characters specified by the arguments. This relationship is used by routines which perform uppercase and lowercase conversions. The first integer ch_up is the ascii value of the uppercase character and the second parameter ch_low is the ascii value of its lowercase counterpart.

See Also

strlow, strup

9.6 double

Synopsis

Convert an object to double precision

Usage

result = double (x)

Description

The double function typecasts an object x to double precision. For example, if x is an array of integers, an array of double types will be returned. If an object cannot be converted to Double_Type, a type-mismatch error will result.

Notes

The double function is equivalent to the typecast operation

     typecast (x, Double_Type)
To convert a string to a double precision number, use atoi function.
See Also

typecast, atoi, int

9.7 int

Synopsis

Typecast an object to an integer

Usage

int (s)

Description

This function performs a typecast of s from its data type to an object of Integer_Type. If s is a string, it returns returns the ascii value value of the first character of the string s. If s is Double_Type, int truncates the number to an integer and returns it.

Example

int can be used to convert single character strings to integers. As an example, the intrinsic function isdigit may be defined as

    define isdigit (s)
    {
      if ((int (s) >= '0') and (int (s) <= '9')) return 1;
      return 0;
    }
Notes

This function is equalent to typecast (s, Integer_Type);

See Also

typecast, double, integer, char, isdigit

9.8 integer

Synopsis

Convert a string to an integer

Usage

Integer_Type integer (String_Type s)

Description

The integer function converts a string representation of an integer back to an integer. If the string does not form a valid integer, a type-mismatch error will be generated.

Example

integer ("1234") returns the integer value 1234.

Notes

This function operates only on strings and is not the same as the more general typecast operator.

See Also

typecast, _slang_guess_type, string, sprintf, char

9.9 isdigit

Synopsis

Tests for a decimal digit character

Usage

Integer_Type isdigit (String_Type s)

Description

This function returns a non-zero value if the first character in the string s is a digit; otherwise, it returns zero.

Example

A simple, user defined implementation of isdigit is

    define isdigit (s)
    {
       return ((s[0] <= '9') and (s[0]  >= '0'));
    }
However, the intrinsic function isdigit executes many times faster than the equivalent representation defined above.
Notes

Unlike the C function with the same name, the S-lang function takes a string argument.

See Also

int, integer

9.10 string

Synopsis

Convert an object to a string representation.

Usage

Integer_Type string (obj)

Description

The string function may be used to convert an object obj of any type to a string representation. For example, string(12.34) returns "12.34".

Example

     define print_anything (anything)
     {
        message (string (anything));
     }
Notes

This function is not the same as typecasting to a String_Type using the typecast function.

See Also

typecast, sprintf, integer, char

9.11 tolower

Synopsis

Convert a character to lowercase.

Usage

Integer_Type lower (Integer_Type ch)

Description

This function takes an integer ch and returns its lowercase equivalent.

See Also

toupper, strup, strlow, int, char, define_case

9.12 toupper

Synopsis

Convert a character to uppercase.

Usage

Integer_Type toupper (Integer_Type ch)

Description

This function takes an integer ch and returns its uppercase equivalent.

See Also

tolower, strup, strlow, int, char, define_case

9.13 typecast

Synopsis

Convert an object from one data type to another.

Usage

typecast (x, new_type)

Description

The typecast function performs a generic typecast operation on x to convert it to new_type. If x represents an array, the function will attempt to convert all elements of x to new_type. Not all objects can be converted and a type-mismatch error will result upon failure.

Example

    define to_complex (x)
    {
       return typecast (x, Complex_Type);
    }
defines a function that converts its argument, x to a complex number.
See Also

int, double, typeof

9.14 typeof

Synopsis

Get the data type of an object.

Usage

DataType_Type typeof (x)

Description

This function returns the data type of x.

Example

  if (Integer_Type == typeof (x)) message ("x is an integer");
See Also

_typeof, array_info, _slang_guess_type, typecast

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Stdio File I/O Functions

 Next Previous Contents

10. Stdio File I/O Functions

10.1 clearerr

Synopsis

Clear the error of a file stream

Usage

clearerr (File_Type fp

Description

The clearerr function clears the error and end-of-file flags associated with the open file stream fp.

See Also

ferror, feof, fopen

10.2 fclose

Synopsis

Close a file

Usage

Integer_Type fclose (File_Type fp)

Description

The fclose function may be used to close an open file pointer fp. Upon success it returns zero, and upon failure it sets errno and returns -1. Failure usually indicates a that the file system is full or that fp does not refer to an open file.

Notes

Many C programmers call fclose without checking the return value. The S-lang language requires the programmer to explicitly handle any value returned by a S-lang function. The simplest way to handle the return value from fclose is to use it as:

     () = fclose (fp);
See Also

fopen, fgets, fflush, pclose, errno

10.3 fdopen

Synopsis

Convert a FD_Type file descriptor to a stdio File_Type object

Usage

File_Type fdopen (FD_Type, String_Type mode)

Description

The fdopen function creates and returns a stdio File_Type object from the open FD_Type descriptor fd. The mode parameter corresponds to the mode parameter of the fopen function and must be consistent with the mode of the descriptor fd. The function returns NULL upon failure and sets errno.

Notes

The fclose function does not close the File_Type object returned from this function. The underlying file object must be closed by the close function.

See Also

fileno, fopen, open, close, fclose

10.4 feof

Synopsis

Get the end-of-file status

Usage

Integer_Type feof (File_Type fp)

Description

This function may be used to determine the state of the end-of-file indicator of the open file descriptor fp. It returns 0 if the indicator is not set, or non-zero if it is. The end-of-file indicator may be cleared by the clearerr function.

See Also

ferror, clearerr, fopen

10.5 ferror

Synopsis

Determine the error status of an open file descriptor

Usage

Integer_Type ferror (File_Type fp)

Description

This function may be used to determine the state of the error indicator of the open file descriptor fp. It returns 0 if the indicator is not set, or non-zero if it is. The error indicator may be cleared by the clearerr function.

See Also

feof, clearerr, fopen

10.6 fflush

Synopsis

Flush an output stream

Usage

Integer_Type fflush (File_Type fp)

Description

The fflush function may be used to update the output stream specified by fp. It returns 0 upon success, or -1 upon failure and sets errno accordingly. In particular, this function will fail if fp does not represent an output stream, or if fp is associated with a disk file and there is insufficient disk space.

Example

This example illustrates how to use the fflush function without regard to the return value:

    () = fputs ("Enter value> ", stdout);
    () = fflush (stdout);
Notes

Many C programmers disregard the return value from the fflush function. The above example illustrates how to properly do this in the S-lang langauge.

See Also

fopen, fclose

10.7 fgets

Synopsis

Read a line from a file.

Usage

Integer_Type fgets (SLang_Ref_Type ref, File_Type fp)

Description

fgets reads a line from the open file specified by fp and places the characters in the variable whose reference is specified by ref. It returns -1 if fp is not associated with an open file or an attempt was made to read at the end the file; otherwise, it returns the number of characters read.

Example

The following example returns the lines of a file via a linked list:

    define read_file (file)
    {
       variable buf, fp, root, tail;
       variable list_type = struct { text, next };

       root = NULL;

       fp = fopen(file, "r");
       if (fp == NULL)
         error("fopen %s failed." file);
       while (-1 != fgets (&buf, fp))
         {
            if (root == NULL)
              {
                 root = @list_type;
                 tail = root;
              }
            else
              {
                 tail.next = @list_type;
                 tail = tail.next;
              }
            tail.text = buf;
            tail.next = NULL;
         }
       () = fclose (fp);
       return root;
    }
See Also

fopen, fclose, fputs, fread, error

10.8 fgetslines

Synopsis

Read all the lines from an open file

Usage

String_Type[] fgetslines (File_Type fp)

Description

The fgetslines function returns all the remaining lines as an array of strings in the file specified by the open file pointer fp. If the file is empty, an empty string array will be returned. The function returns NULL upon error.

Example

The following function returns the number of lines in a file:

    define count_lines_in_file (file)
    {
       variable fp, lines;

       fp = fopen (file, "r");
       if (fp == NULL)
         return -1;

       lines = fgetslines (fp);
       if (lines == NULL)
         return -1;

       return length (lines);
    }
Note that the file was implicitly closed by the function.
Notes

This function should not be used if the file contains many lines since that would require that all the lines be read into memory.

See Also

fgets, fread, fopen

10.9 fopen

Synopsis

Open a file

Usage

File_Type fopen (String_Type f, String_Type m)

Description

The fopen function opens a file f according to the mode string m. Allowed values for m are:

     "r"    Read only
     "w"    Write only
     "a"    Append
     "r+"   Reading and writing at the beginning of the file.
     "w+"   Reading and writing.  The file is created if it does not
              exist; otherwise, it is truncated.
     "a+"   Reading and writing at the end of the file.  The file is created
              if it does not already exist.
In addition, the mode string can also include the letter 'b' as the last character to indicate that the file is to be opened in binary mode.

Upon success, fopen a File_Type object which is meant to be used in other operations that require an open file. Upon failure, the function returns NULL.

Example

The following function opens a file in append mode and writes a string to it:

    define append_string_to_file (file, str)
    {
       variable fp = fopen (file, "a");
       if (fp == NULL) verror ("%s could not be opened", file);
       () = fputs (string, fp);
       () = fclose (fp);
    }
Note that the return values from fputs and fclose are ignored.
Notes

There is no need to explicitly close a file opened with fopen. If the returned File_Type object goes out of scope, S-lang will automatically close the file. However, explicitly closing a file after use is recommended.

See Also

fclose, fgets, fputs, popen

10.10 fprintf

Synopsis

Create and write a formatted string to a file

Usage

Int_Type fprintf (File_Type fp, String_Type fmt, ...)

Description

fprintf formats the objects specified by the variable argument list according to the format fmt and write the result to the open file pointer fp.

The format string obeys the same syntax and semantics as the sprintf format string. See the description of the sprintf function for more information.

fprintf returns the number of characters written to the file, or -1 upon error.

See Also

fputs, printf, fwrite, message

10.11 fputs

Synopsis

Write a string to an open stream

Usage

Integer_Type fputs (String_Type s, File_Type fp);

Description

The fputs function writes the string s to the open file pointer fp. It returns -1 upon failure and sets errno, otherwise it returns the length of the string.

Example

The following function opens a file in append mode and uses the fputs function to write to it.

    define append_string_to_file (str, file)
    {
       variable fp;
       fp = fopen (file, "a");
       if (fp == NULL) verror ("Unable to open %s", file);
       if ((-1 == fputs (s, fp))
           or (-1 == fclose (fp)))
         verror ("Error writing to %s", file);
    }
Notes

One must not disregard the return value from the fputs function, as many C programmers do. Doing so may lead to a stack overflow error.

To write an object that contains embedded null characters, use the fwrite function.

See Also

fclose, fopen, fgets, fwrite

10.12 fread

Synopsis

Read binary data from a file

Usage

UInt_Type fread (Ref_Type b, DataType_Type t, UInt_Type n, File_Type fp)

Description

The fread function may be used to read n objects of type t from an open file pointer fp. Upon success, it returns the number of objects read from the file and places the objects in the variable specified by b. Upon error or end of file, it returns -1. If more than one object is read from the file, those objects will be placed in an array of the appropriate size. The exception to this is when reading Char_Type or UChar_Type objects from a file, in which case the data will be returned as a BString_Type binary string.

Example

The following example illustrates how to read 50 bytes from a file:

     define read_50_bytes_from_file (file)
     {
        variable fp, n, buf;

        fp = fopen (file, "rb");
        if (fp == NULL) error ("Open failed");
        n = fread (&buf, Char_Type, 50, fp);
        if (n == -1)
          error ("fread failed");
        () = fclose (fp);
        return buf;
     }
Notes

Use the pack and unpack functions to read data with a specific byte-ordering.

See Also

fwrite, fgets, fopen, pack, unpack

10.13 fseek

Synopsis

Reposition a stream

Usage

Integer_Type fseek (File_Type fp, Integer_Type ofs, Integer_Type whence

Description

The fseek function may be used to reposition the file position pointer associated with the open file stream fp. Specifically, it moves the pointer ofs bytes relative to the position indicated by whence. If whence is set to one of the symbolic constants SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to the start of the file, the current position indicator, or end-of-file, respectively.

The function return zero upon success, or -1 upon failure and sets errno accordingly.

Example

define rewind (fp)
{
if (0 == fseek (fp, 0, SEEK_SET)) return;
vmessage ("rewind failed, reason: %s", errno_string (errno));
}

Notes

The current implementation uses an integer to specify the offset. One some systems, a long integer may be required making this function fail for very large files, i.e., files that are longer than the maximum value of an integer.

See Also

ftell, fopen

10.14 ftell

Synopsis

Obtain the current position in an open stream

Usage

Integer_Type ftell (File_Type fp)

Description

The ftell function may be used to obtain the current position in the stream associated with the open file pointer fp. It returns the position of the pointer measured in bytes from the beginning of the file. Upon error, it returns -1 and sets errno.

See Also

fseek, fopen

10.15 fwrite

Synopsis

Write binary data to a file

Usage

UInt_Type fwrite (b, File_Type fp)

Description

The fwrite may be used to write the object represented by b to an open file. If b is a string or an array, the function will attempt to write all elements of the object to the file. It returns the number of objects successfully written, otherwise it returns -1 upon error and sets errno accordingly.

Example

The following example illustrates how to write an integer array to a file. In this example, fp is an open file descriptor:

     variable a = [1:50];     % 50 element integer array
     if (50 != fwrite (a, fp))
       error ("fwrite failed");
Here is how to write the array one element at a time:
     variable a = [1:50];
     foreach (a)
       {
          variable ai = ();
          if (1 != fwrite(ai, fp))
            error ("fwrite failed");
       }
Notes

Not all data types may support the fwrite operation. However, it is supported by all vector, scalar, and string objects.

See Also

fread, fputs, fopen, pack, unpack

10.16 pclose

Synopsis

Close an object opened with popen

Usage

Integer_Type pclose (File_Type fp)

Description

The pclose function waits for the process associated with fp to exit and the returns the exit status of the command.

See Also

pclose, fclose

10.17 popen

Synopsis

Open a process

Usage

File_Type popen (String_Type cmd, String_Type mode)

Description

The popen function executes a process specified by cmd and opens a unidirectional pipe to the newly created process. The mode indicates whether or not the the pipe is open for reading or writing. Specifically, if mode is "r", then the pipe is opened for reading, or if mode is "w", then the pipe will be open for writing.

Upon success, a File_Type pointer will be returned, otherwise the function failed and NULL will be returned.

Notes

This function is not available on all systems.

See Also

pclose, fopen

10.18 printf

Synopsis

Create and write a formatted string to stdout

Usage

Int_Type printf (String_Type fmt, ...)

Description

fprintf formats the objects specified by the variable argument list according to the format fmt and write the result to stdout. This function is equivalent to fprintf used with the stdout file pointer. See fprintf for more information.

printf returns the number of characters written to the file, or -1 upon error.

Notes

Many C programmers do not check the return status of the printf C library function. Make sure that if you do not care about whether or not the function succeeds, then code it as in the following example:

     () = printf ("%s laid %d eggs\n", chicken_name, num_egg);
See Also

fputs, printf, fwrite, message

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Low-level POSIX I/O functions

 Next Previous Contents

11. Low-level POSIX I/O functions

11.1 close

Synopsis

Close an open file descriptor

Usage

Int_Type close (FD_Type fd)

Description

The close function is used to open file descriptor of type FD_Type. Upon success 0 is returned, otherwise the function returns -1 and sets errno accordingly.

See Also

open, fclose, read, write

11.2 fileno

Synopsis

Convert a stdio File_Type object to a FD_Type descriptor

Usage

FD_Type fileno (File_Type fp)

Description

The fileno function returns the FD_Type descriptor associated with the File_Type file pointer. Upon failure, NULL is returned.

See Also

fopen, open, fclose, close

11.3 isatty

Synopsis

Determine if an open file descriptor refers to a terminal

Usage

Int_Type isatty (FD_Type or File_Type fd)

Description

This function returns 1 if the file descriptor fd refers to a terminal; otherwise it returns 0. The object fd may either be a File_Type stdio descriptor or an FD_Type object.

See Also

fopen, fclose, fileno

11.4 lseek

Synopsis

Reposition a file descriptor's file pointer

Usage

Long_Type lseek (FD_Type fd, Long_Type ofs, int mode) The lseek function repositions the file pointer associated with the open file descriptor fp to offset ofs according to the mode parameter. Specifically, mode must be one of the values:

     SEEK_SET   Set the offset to ofs
     SEEK_CUR   Add ofs to the current offset
     SEEK_END   Add ofs to the current file size
Upon error, lseek returns -1 and sets errno. If successful, it returns the new filepointer offset.
Notes

Not all file descriptors are capable of supporting the seek operation, e.g., a descriptor associated with a pipe.

By using SEEK_END with a positive value of the ofs parameter, it is possible to position the file pointer beyond the current size of the file.

See Also

fseek, ftell, open, close

11.5 open

Synopsis

Open a file

Usage

FD_Type open (String_Type filename, Int_Type flags [,Int_Type mode])

Description

The open function attempts to open a file specified by the filename parameter according to the flags parameter, which must be one of the following values:

     O_RDONLY   (read-only)
     O_WRONLY   (write-only)
     O_RDWR     (read/write)
In addition, flags may also be bitwise-or'd with any of the following:
     O_BINARY   (open the file in binary mode)
     O_TEXT     (open the file in text mode)
     O_CREAT    (create file if it does not exists)
     O_EXCL     (fail if the file already exists)
     O_NOCTTY   (do not make the device the controlling terminal)
     O_TRUNC    (truncate the file if it exists)
     O_APPEND   (open the file in append mode)
     O_NONBLOCK (open the file in non-blocking mode)
If O_CREAT is used for the flags parameterm then the mode parameter must be present. mode specifies the permissions to use if a new file is created. The actual file permissions will be affected by the process's umask via mode&~umask. The mode parameter's value is constructed via bitwise-or of the following values:
     S_IRWXU    (Owner has read/write/execute permission)
     S_IRUSR    (Owner has read permission)
     S_IWUSR    (Owner has write permission)
     S_IXUSR    (Owner has execute permission)
     S_IRWXG    (Group has read/write/execute permission)
     S_IRGRP    (Group has read permission)
     S_IWGRP    (Group has write permission)
     S_IXGRP    (Group has execute permission)
     S_IRWXO    (Others have read/write/execute permission)
     S_IROTH    (Others have read permission)
     S_IWOTH    (Others have write permission)
     S_IXOTH    (Others have execute permission)
Upon success open returns a file descriptor object (FD_Type), otherwise NULL is returned and errno is set.
Notes

If you are not familiar with the open system call, then it is recommended that you use fopen instead.

See Also

fopen, close, read, write, stat_file

11.6 read

Synopsis

Read from an open file descriptor

Usage

UInt_Type read (FD_Type fd, Ref_Type buf, UInt_Type num)

Description

The read function attempts to read at most num bytes into the variable indicated by buf from the open file descriptor fd. It returns the number of bytes read, or -1 and sets errno upon failure. The number of bytes read may be less than num, and will be zero if an attempt is made to read past the end of the file.

Notes

read is a low-level function and may return -1 for a variety of reasons. For example, if non-blocking I/O has been specified for the open file descriptor and no data is available for reading then the function will return -1 and set errno to EAGAIN.

See Also

fread, open, close, write

11.7 write

Synopsis

Write to an open file descriptor

Usage

UInt_Type write (FD_Type fd, BString_Type buf

Description

The write function attempts to write the bytes specified by the buf parameter to the open file descriptor fd. It returns the number of bytes successfully written, or -1 and sets errno upon failure. The number of bytes written may be less than length(buf).

See Also

read, fwrite, open, close

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Directory Functions

 Next Previous Contents

12. Directory Functions

12.1 chdir

Synopsis

Change the current working directory.

Usage

Integer_Type chdir (String_Type dir)

Description

The chdir function may be used to changed the current working directory to the directory specified by dir. Upon sucess it returns zero; however, upon failure it returns -1 and sets errno accordingly.

See Also

mkdir, stat_file

12.2 chmod

Synopsis

Change the mode of a file

Usage

Integer_Type chmod (String_Type file, Integer_Type mode)

Description

The chmod function changes the permissions of file to those specified by mode. It returns 0 upon success, or -1 upon failure setting errno accordingly.

See the system specific documentation for the C library function chmod for a discussion of the mode parameter.

See Also

chown, stat_file

12.3 chown

Synopsis

Change the owner of a file

Usage

Integer_Type chown (String_Type file, Integer_Type uid, Integer_Type gid)

Description

The chown function is used to change the user-id and group-id of file to uid and gid, respectively. It returns zero upon success and -1 upon failure, with errno set accordingly.

Notes

On most systems, only the super user can change the ownership of a file.

Some systems do not support this function.

See Also

chmod, stat_file

12.4 getcwd

Synopsis

Get the current working directory

Usage

String_Type getcwd ()

Description

The getcwd function returns the absolute pathname of the current working directory. If an error occurs or it cannot determine the working directory, it returns NULL and sets errno accordingly.

Notes

Under Unix, OS/2, and MSDOS, the pathname returned by this function includes the trailing slash character. Some versions also include the drive specifier.

See Also

mkdir, chdir, errno

12.5 listdir

Synopsis

Get a list of the files in a directory

Usage

String_Type[] listdir (String_Type dir)

Description

The listdir function returns the directory listing of all the files in the specified directory dir as an array of strings. It does not return the special files ".." and "." as part of the list.

See Also

stat_file, stat_is, length

12.6 lstat_file

Synopsis

Get information about a symbolic link

Usage

Struct_Type lstat_file (String_Type file)

Description

The lstat_file function behaves identically to stat_file but if file is a symbolic link, lstat_file returns information about the link itself, and not the file that it references.

See the documentation for stat_file for more information.

Notes

On systems that do not support symbolic links, there is no difference between this function and the stat_file function.

See Also

stat_file, readlink

12.7 mkdir

Synopsis

Create a new directory

Usage

Integer_Type mkdir (String_Type dir, Integer_Type mode)

Description

The mkdir function creates a directory whose name is specified by the dir parameter with permissions specified by mode. Upon success mkdir returns zero, or it returns -1 and sets errno accordingly. In particular, if the directory already exists, the function will fail and set errno to EEXIST.

Example

     define my_mkdir (dir)
     {
        if (0 == mkdir (dir, 0777)) return;
        if (errno == EEXIST) return;
        verror ("mkdir %s failed: %s", dir, errno_string (errno));
     }
Notes

The mode parameter may not be meaningful on all systems. On systems where it is meaningful, the actual permissions on the newly created directory are modified by the process's umask.

See Also

rmdir, getcwd, chdir, fopen, errno

12.8 readlink

Synopsis

String_Type readlink (String_Type path)

Usage

Get the value of a symbolic link

Description

The readlink function returns the value of a symbolic link and returns it as a string. Upon failure, NULL is returned and errno set accordingly.

Notes

Not all systems support this function.

See Also

lstat_file, stat_file, stat_is

12.9 remove

Synopsis

Delete a file

Usage

Integer_Type remove (String_Type file)

Description

The remove function deletes a file. It returns 0 upon success, or -1 upon error and sets errno accordingly.

See Also

rename, rmdir

12.10 rename

Synopsis

Rename a file

Usage

Integer_Type rename (String_Type old, String_Type new)

Description

The rename function renames a file from old to new moving it between directories if necessary. This function may fail if the directories do not refer to the same file system. It returns 0 upon success, or -1 upon error and sets errno accordingly.

See Also

remove, errno

12.11 rmdir

Synopsis

Remove a directory

Usage

Integer_Type rmdir (String_Type dir)

Description

The rmdir function deletes a specified directory. It returns 0 upon success or -1 upon error and sets errno accordingly.

Notes

The directory must be empty before it can be removed.

See Also

rename, remove, mkdir

12.12 stat_file

Synopsis

Get information about a file

Usage

Struct_Type stat_file (String_Type file)

Description

The stat_file function returns information about file through the use of the system stat call. If the stat call fails, the function returns NULL and sets errno accordingly. If it is successful, it returns a stat structure with the following integer fields:

    st_dev
    st_ino
    st_mode
    st_nlink
    st_uid
    st_gid
    st_rdev
    st_size
    st_atime
    st_mtime
    st_ctime
See the man page for stat for a discussion of these fields.
Example

The following example shows how the stat_file function may be used to get the size of a file:

     define file_size (file)
     {
        variable st;
        st = stat_file(file);
        if (st == NULL) verror ("Unable to stat %s", file);
        return st.st_size;
     }
See Also

lstat_file, stat_is

12.13 stat_is

Synopsis

Parse the st_mode field of a stat structure

Usage

Char_Type stat_is (String_Type type, Integer_Type st_mode)

Description

The stat_is function returns a signed character value about the type of file specified by st_mode. Specifically, type must be one of the strings:

     "sock"     (socket)
     "fifo"     (fifo)
     "blk"      (block device)
     "chr"      (character device)
     "reg"      (regular file)
     "lnk"      (link)
     "dir"      (dir)
It returns a non-zero value if st_mode corresponds to type.
Example

The following example illustrates how to use the stat_is function to determine whether or not a file is a directory:

     define is_directory (file)
     {
        variable st;

        st = stat_file (file);
        if (st == NULL) return 0;
        return stat_is ("dir", st.st_mode);
     }
See Also

stat_file, lstat_file

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Functions that parse pathnames

 Next Previous Contents

13. Functions that parse pathnames

13.1 path_basename

Synopsis

Get the basename part of a pathname

Usage

String_Type path_basename (String_Type path)

Description

The path_basename function returns the basename associated with the path parameter. The basename is the non-directory part of the filename, e.g., on unix c is the basename of /a/b/c.

See Also

path_dirname, path_extname, path_concat, path_is_absolute

13.2 path_concat

Synopsis

Combine elements of a pathname

Usage

String_Type path_concat (String_Type dir, String_Type basename)

Description

The path_concat function combines the arguments dir and basename to produce a pathname. For example, on unix is dir is x/y and basename is z, then the function will return x/y/z.

See Also

path_dirname, path_basename, path_extname, path_is_absolute

13.3 path_dirname

Synopsis

Get the directory name part of a pathname

Usage

String_Type path_dirname (String_Type path)

Description

The path_dirname function returns the directory name associated with a specified pathname.

Notes

On systems that include a drive specifier as part of the pathname, the value returned by this function will include the driver specifier.

See Also

path_basename, path_extname, path_concat, path_is_absolute

13.4 path_extname

Synopsis

Return the extension part of a pathname

Usage

String_Type path_extname (String_Type path)

Description

The path_extname function returns the extension portion of a specified pathname. If an extension is present, this function will also include the dot as part of the extension, i.e., if path is file.c, then this function returns ".c". If no extension is present, the function returns an empty string "".

Notes

Under VMS, the file version number is not returned as part of the extension.

See Also

path_dirname, path_basename, path_concat, path_is_absolute

13.5 path_is_absolute

Synopsis

Determine whether or not a pathname is absolute

Usage

Int_Type path_is_absolute (String_Type path)

Description

The path_is_absolute function will return non-zero is path refers to an absolute pathname, otherwise it returns zero.

See Also

path_dirname, path_basename, path_extname, path_concat

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: System Call Functions

 Next Previous Contents

14. System Call Functions

14.1 errno

Synopsis

Error code set by system functions.

Usage

Integer_Type errno

Description

A system function can fail for a variety of reasons. For example, a file operation may fail because lack of disk space, or the process does not have permission to perform the operation. Such functions will return -1 and set the variable errno to an error code describing the reason for failure.

Particular values of errno may be specified by the following symbolic constants (read-only variables) and the corresponding errno_string value:

     EPERM            "Not owner"
     ENOENT           "No such file or directory"
     ESRCH            "No such process"
     ENXIO            "No such device or address"
     ENOEXEC          "Exec format error"
     EBADF            "Bad file number"
     ECHILD           "No children"
     ENOMEM           "Not enough core"
     EACCES           "Permission denied"
     EFAULT           "Bad address"
     ENOTBLK          "Block device required"
     EBUSY            "Mount device busy"
     EEXIST           "File exists"
     EXDEV            "Cross-device link"
     ENODEV           "No such device"
     ENOTDIR          "Not a directory"
     EISDIR           "Is a directory"
     EINVAL           "Invalid argument"
     ENFILE           "File table overflow"
     EMFILE           "Too many open files"
     ENOTTY           "Not a typewriter"
     ETXTBSY          "Text file busy"
     EFBIG            "File too large"
     ENOSPC           "No space left on device"
     ESPIPE           "Illegal seek"
     EROFS            "Read-only file system"
     EMLINK           "Too many links"
     EPIPE            "Broken pipe"
     ELOOP            "Too many levels of symbolic links"
     ENAMETOOLONG     "File name too long"
Example

The mkdir function will attempt to create a directory. If that directory already exists, the function will fail and set errno to EEXIST.

    define create_dir (dir)
    {
       if (0 == mkdir (dir)) return;
       if (errno != EEXIST)
         error ("mkdir %s failied: %s", dir, errno_string);
    }
See Also

errno_string, error, mkdir

14.2 errno_string

Synopsis

Return a string describing an errno.

Usage

String_Type errno_string (Integer_Type err)

Description

The errno_string function returns a string describing the integer error code err. The variable err usually corresponds to the errno intrinsic function. See the description for errno for more information.

Example

The errno_string function may be used as follows:

    define sizeof_file (file)
    {
       variable st = stat (file);
       if (st == NULL)
         verror ("%s: %s", file, errno_string (errno);
       return st.st_size;
    }
See Also

errno, stat, verror

14.3 getegid

Synopsis

Get the effective group id

Usage

Int_Type getegid ()

Description

The getegid function returns the effective group ID of the current process.

Notes

This function is not supported by all systems.

See Also

getgid, geteuid, setgid

14.4 geteuid

Synopsis

Get the effective user-id of the current process

Usage

Int_Type geteuid ()

Description

The geteuid function returns the effective user-id of the current process.

Notes

This function is not supported by all systems.

See Also

getuid, setuid, setgid

14.5 getgid

Synopsis

Get the group id

Usage

Integer_Type getgid ()

Description

The getgid function returns the real group id of the current process.

Notes

This function is not supported by all systems.

See Also

getpid, getppid

14.6 getpid

Synopsis

Get the current process id

Usage

Integer_Type getpid ()

Description

The getpid function returns the current process identification number.

See Also

getppid, getgid

14.7 getppid

Synopsis

Get the parent process id

Usage

Integer_Type getppid ()

Description

The getpid function returns the process identification number of the parent process.

Notes

This function is not supported by all systems.

See Also

getpid, getgid

14.8 getuid

Synopsis

Get the user-id of the current process

Usage

Int_Type getuid ()

Description

The getuid function returns the user-id of the current process.

Notes

This function is not supported by all systems.

See Also

getuid, getegid

14.9 kill

Synopsis

Send a signal to a process

Usage

Integer_Type kill (Integer_Type pid, Integer_Type sig)

Description

This function may be used to send a signal given by the integer sig to the process specified by pid. The function returns zero upon sucess and -1 upon failure setting errno accordingly.

Example

The kill function may be used to determine whether or not a specific process exists:

    define process_exists (pid)
    {
       if (-1 == kill (pid, 0))
         return 0;     % Process does not exist
       return 1;
    }
Notes

This function is not supported by all systems.

See Also

getpid

14.10 mkfifo

Synopsis

Create a named pipe

Usage

Int_Type mkfifo (String_Type name, Int_Type mode)

Description

The mkfifo attempts to create a named pipe with the specified name and mode (modified by the process's umask). The function returns 0 upon success, or -1 and sets errno upon failure.

Notes

Not all systems support the mkfifo function and even on systems that do implement the mkfifo system call, the underlying file system may not support the concept of a named pipe, e.g, an NFS filesystem.

See Also

stat_file

14.11 setgid

Synopsis

Set the group-id of the current process

Usage

Int_Type setgid (Int_Type gid)

Description

The setgid function sets the effective group-id of the current process. It returns zero upon success, or -1 upon error and sets errno appropriately.

Notes

This function is not supported by all systems.

See Also

getgid, setuid

14.12 setpgid

Synopsis

Set the process group-id

Usage

Int_Type setpgid (Int_Type pid, Int_Type gid)

Description

The setpgid function sets the group-id gid of the process whose process-id is pid. If pid is 0, then the current process-id will be used. If pgid is 0, then the pid of the affected process will be used.

If successful zero will be returned, otherwise the function will return -1 and set errno accordingly.

Notes

This function is not supported by all systems.

See Also

setgid, setuid

14.13 setuid

Synopsis

Set the user-id of the current process

Usage

Int_Type setuid (Int_Type id)

Description

The setuid function sets the effective user-id of the current process. It returns zero upon success, or -1 upon error and sets errno appropriately.

Notes

This function is not supported by all systems.

See Also

setgid, setpgid, getuid, geteuid

14.14 sleep

Synopsis

Pause for a specified number of seconds

Usage

sleep (UInt_Type n)

Description

The sleep function delays the current process for the specified number of seconds. If it is interrupted by a signal, it will return prematurely.

14.15 system

Synopsis

Execute a shell command

Usage

Integer_Type system (String_Type cmd)

Description

The system function may be used to execute the string expression cmd in an inferior shell. This function is an interface to the C system function which returns an implementation-defined result. On Linux, it returns 127 if the inferior shell could not be invoked, -1 if there was some other error, otherwise it returns the return code for cmd.

Example

    define dir ()
    {
       () = system ("DIR");
    }
displays a directory listing of the current directory under MSDOS or VMS.
See Also

popen, listdir

14.16 umask

Synopsis

Set the file creation mask

Usage

Int_Type umask (Int_Type m)

Description

The umask function sets the file creation mask to m and returns the previous mask.

See Also

stat_file

14.17 uname

Synopsis

Get the system name

Usage

Struct_Tye uname ()

Description

The uname function returns a structure containing information about the operating system. The structure contains the following fields:

       sysname  (Name of the operating system)
       nodename (Name of the node within the network)
       release  (Release level of the OS)
       version  (Current version of the release)
       machine  (Name of the hardware)
Notes

Not all systems support this function.

See Also

getenv, pack, unpack

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Eval Functions

 Next Previous Contents

15. Eval Functions

15.1 autoload

Synopsis

Load a function from a file

Usage

autoload (String_Type funct, String_Type file)

Description

The autoload function is used to declare funct to the interpreter and indicate that it should be loaded from file when it is actually used.

Example

Suppose bessel_j0 is a function defined in the file bessel.sl. Then the statement

      autoload ("bessel_j0", "bessel.sl");
will cause bessel.sl to be loaded prior to the execution of bessel_j0
See Also

evalfile

15.2 byte_compile_file

Synopsis

Compile a file to byte-code for faster loading.

Usage

byte_compile_file (String_Type file, Integer_Type method)

Description

The byte_compile_file function byte-compiles file producing a new file with the same name except a 'c' is added to the output file name. For example, file is "site.sl", then the function produces a new file named site.slc.

Notes

The method parameter is not used in the current implementation. Its use is reserved for the future. For now, set it to 0.

See Also

evalfile

15.3 eval

Synopsis

Interpret a string as S-lang code

Usage

eval (String_Type expression)

Description

The eval function parses a string as S-Lang code and executes the result. This is a useful function in many contexts such as dynamically generating function definitions where there is no way to generate them otherwise.

Example

    if (0 == is_defined ("my_function"))
      eval ("define my_function () { message (\"my_function\"); }");
See Also

is_defined, autoload, evalfile

15.4 evalfile

Synopsis

Interpret a file containing S-lang code.

Usage

Integer_Type evalfile (String_Type file)

Description

The evalfile function loads file into the interpreter. If no errors were encountered, 1 will be returned; otherwise, a S-lang error will be generated and the function will return zero.

Example

    define load_file (file)
    {
       ERROR_BLOCK { _clear_error (); }
       () = evalfile (file);
    }
See Also

eval, autoload

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Module Functions

 Next Previous Contents

16. Module Functions

16.1 get_import_module_path

Synopsis

Get the search path for dynamically loadable objects

Usage

String_Type get_import_module_path ()

Description

The get_import_module_path may be used to get the search path for dynamically shared objects. Such objects may be made accessable to the application via the import function.

See Also

import, set_import_module_path

16.2 import

Synopsis

Dynamically link to a specified module

Usage

import (String_Type module)

Description

The import function causes the run-time linker to dynamically link to the specified shared object. It seaches for the shared object as follows: First a search is performed along all module paths specified by the application. Then a search is made along the paths defined via the set_import_module_path function. If not found, a search is performed along the paths given by the SLANG_MODULE_PATH environment variable. Finally, a system dependent search is performed (e.g., using the LD_LIBRARY_PATH environment variable).

This function signals an error if the specified module is not found.

Notes

The import function is not available on all systems.

See Also

set_import_module_path, getenv, evalfile

16.3 set_import_module_path

Synopsis

Set the search path for dynamically loadable objects

Usage

set_import_module_path (String_Type path_list)

Description

The set_import_module_path may be used to set the search path for dynamically shared objects. Such objects may be made accessable to the application via the import function.

The actual syntax for the specification of the set of paths will vary according to the operating system. Under Unix, a colon character is used to separate paths in path_list. For win32 systems a semi-colon is used.

See Also

import, get_import_module_path

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Debugging Functions

 Next Previous Contents

17. Debugging Functions

17.1 _clear_error

Synopsis

Clear an error condition

Usage

_clear_error ()

Description

This function may be used in error-blocks to clear the error that triggered execution of the error block. Execution resumes following the statement, in the scope of the error-block, that triggered the error.

Example

Consider the following wrapper around the putenv function:

    define try_putenv (name, value)
    {
       variable status;
       ERROR_BLOCK
        {
          _clear_error ();
          status = -1;
        }
       status = 0;
       putenv (sprintf ("%s=%s", name, value);
       return status;
    }
If putenv fails, it generates an error condition, which the try_putenv function catches and clears. Thus try_putenv is a function that returns -1 upon failure and 0 upon success.
See Also

_trace_function, _slangtrace, _traceback

17.2 _debug_info

Synopsis

Configure debugging information

Usage

Integer_Type _debug_info

Description

The _debug_info variable controls whether or not extra code should be generated for additional debugging and traceback information. Currently, if _debug_info is zero, no extra code will be generated; otherwise extra code will be inserted into the compiled bytecode for additional debugging data.

The value of this variable is is local to each compilation unit and setting its value in one unit has no effect upon its value in other units.

Example

    _debug_info = 1;   % Enable debugging information
Notes

Setting this variable to a non-zero value may slow down the interpreter somewhat.

See Also

_traceback, _slangtrace

17.3 _slangtrace

Synopsis

Turn function tracing on or off.

Usage

Integer_Type _slangtrace

Description

The _slangtrace variable is a debugging aid that when set to a non-zero value enables tracing when function declared by _trace_function is entered. If the value is greater than zero, both intrinsic and user defined functions will get traced. However, if set to a value less than zero, intrinsic functions will not get traced.

See Also

_trace_function, _traceback, _print_stack

17.4 _trace_function

Synopsis

Set the function to trace

Usage

_trace_function (String_Type f)

Description

_trace_function declares that the S-lang function with name f is to be traced when it is called. Calling _trace_function does not in itself turn tracing on. Tracing is turned on only when the variable _slangtrace is non-zero.

See Also

_slangtrace, _traceback

17.5 _traceback

Synopsis

Generate a traceback upon error

Usage

Integer_Type _traceback

Description

_traceback is an intrinsic integer variable whose value controls whether or not a traceback of the call stack is to be generated upon error. If _traceback is greater than zero, a full traceback will be generated, which includes the values of local variables. If the value is less than zero, a traceback will be generated without local variable information, and if _traceback is zero the traceback will not be generated.

Local variables are represented in the form $n where n is an integer numbered from zero. More explicitly, $0 represents the first local variable, $1 represents the second, and so on. Please note that function parameters are local variables and that the first parameter corresponds to $0.

See Also

_slangtrace, error

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Stack Functions

 Next Previous Contents

18. Stack Functions

18.1 __pop_args

Synopsis

Remove n function arguments from the stack

Usage

variable args = __pop_args(Integer_Type n);

Description

This function together with the companion function __push_args is useful for passing the arguments of a function to another function. __pop_args returns an array of n structures with a single structure field called value, which represents the value of the argument.

Example

Consider the following print function. It prints all its arguments to stdout separated by spaces:

    define print ()
    {
       variable i;
       variable args = __pop_args (_NARGS);

       for (i = 0; i < _NARGS; i++)
         {
            () = fputs (string (args[i].value), stdout);
            () = fputs (" ", stdout);
         }
       () = fputs ("\n", stdout);
       () = fflush (stdout);
    }
Now consider the problem of defining a function called ones that returns a multi-dimensional array with all the elements set to 1. For example, ones(10) should return a 1-d array of ones, whereas ones(10,20) should return a 10x20 array.
    define ones ()
    {
      !if (_NARGS) return 1;
      variable a;

      a = __pop_args (_NARGS);
      return @Array_Type (Integer_Type, [__push_args (a)]) + 1;
    }
Here, __push_args was used to push on the arguments passed to the ones function onto the stack to be used when dereferencing Array_Type.
See Also

__push_args, typeof, _pop_n

18.2 __push_args

Synopsis

Remove n function arguments onto the stack

Usage

__push_args (Struct_Type args);

Description

This function together with the companion function __pop_args is useful for passing the arguments of one function to another. See the desription of __pop_args for more information.

See Also

__pop_args, typeof, _pop_n

18.3 _pop_n

Synopsis

Remove objects from the stack

Usage

_pop_n (Integer_Type n);

Description

The _pop_n function pops n objects from the top of the stack.

Example

    define add3 ()
    {
       variable x, y, z;
       if (_NARGS != 3)
         {
            _pop_n (_NARGS);
            error ("add3: Expecting 3 arguments");
         }
       (x, y, z) = ();
       return x + y + z;
    }
See Also

_stkdepth, pop

18.4 _print_stack

Synopsis

print the values on the stack.

Usage

_print_stack ()

Description

This function dumps out what is currently on the S-lang. It does not alter the stack and it is usually used for debugging purposes.

See Also

_stkdepth, string

18.5 _stk_reverse

Synopsis

Reverse the order of the objects on the stack.

Usage

_stk_reverse (Integer_Type n)

Description

The _stk_reverse function reverses the order of the top n items on the stack.

See Also

_stkdepth, _stk_roll

18.6 _stk_roll

Synopsis

Roll items on the stack

Usage

_stk_roll (Integer_Type n);

Description

This function may be used to alter the arrangement of objects on the stack. Specifically, if the integer n is positive, the top n items on the stack are rotated up. If n is negative, the top abs(n) items on the stack are rotated down.

Example

If the stack looks like:

    item-0
    item-1
    item-2
    item-3
where item-0 is at the top of the stack, then _stk_roll(-3) will change the stack to:
    item-2
    item-0
    item-1
    item-3
Notes

This function only has an effect for abs(n) > 1.

See Also

_stkdepth, _stk_reverse, _pop_n, _print_stack

18.7 _stkdepth

Usage

Get the number of objects currently on the stack.

Synopsis

Integer_Type _stkdepth ()

Description

The _stkdepth function returns number of items on stack prior to the call of _stkdepth.

See Also

_print_stack, _stk_reverse, _stk_roll

18.8 dup

Synopsis

Duplicate the value at the top of the stack

Usage

dup ()

Description

This function returns an exact duplicate of the object on top of the stack. For some objects such as arrays or structures, it creates a new reference to the array. However, for simple scalar S-Lang types such as strings, integers, and doubles, it creates a new copy of the object.

See Also

pop, typeof

18.9 exch

Synopsis

Exchange two items on the stack

Usage

exch ()

Description

The exch swaps the two top items on the stack.

See Also

pop, _stk_reverse, _stk_roll

18.10 pop

Synopsis

Discard an item from the stack

Usage

pop ()

Description

The pop function removes the top item from the stack.

See Also

_pop_n

Next Previous Contents


S-Lang Run-Time Library Reference: Version 1.4.0: Miscellaneous Functions

 Next Previous Contents

19. Miscellaneous Functions

19.1 __get_reference

Synopsis

Get a reference to a global object

Usage

Ref_Type __get_reference (String_Type nm)

Description

This function returns a reference to a global variable or function whose name is specified by nm. If no such object exists, it returns NULL, otherwise it returns a reference.

Example

For example, consider the function:

    define runhooks (hook)
    {
       variable f;
       f = __get_reference (hook);
       if (f != NULL)
         @f ();
    }
This function could be called from another S-lang function to allow customization of that function, e.g., if the function represents a mode, the hook could be called to setup keybindings for the mode.
See Also

is_defined, typeof, eval, autoload, __is_initialized, __uninitialize

19.2 __uninitialize

Synopsis

Uninitialize a variable

Usage

__uninitialize (Ref_Type x)

Description

The __uninitialize function may be used to uninitialize the variable referenced by the parameter x.

Example

The following two lines are equivalent:

     () = __tmp(z);
     __uninitialize (&z);
See Also

__tmp, __is_initialized

19.3 _auto_declare

Synopsis

Set automatic variable declaration mode

Usage

Integer_Type _auto_declare

Description

The _auto_declare may be used to have all undefined variables implicitely declared as static. If set to zero, any variable must be declared witha variable declaration before it can be used. If set to one, then any undeclared variabled will be declared as a static global variable.

The _auto_declare variable is is local to each compilation unit and setting its value in one unit has no effect upon its value in other units. The value of this variable has no effect upon the variables in a function.

Example

The following code will not compile if X not been declared:

    X = 1;
However,
    _auto_declare = 1;   % declare variables as static.
    X = 1;
is equivalent to
    static variable X = 1;
Notes

This variable should be used sparingly and is intended primarily for interactive applications where one types S-lang commands at a prompt.

19.4 getenv

Synopsis

Get the value of an environment variable

Usage

String_Type getenv(String_Type var)

Description

The getenv function returns a string that represents the value of an environment variable var. It will return NULL if there is no environment variable whose name is given by var.

Example

    if (NULL != getenv ("USE_COLOR"))
      {
        set_color ("normal", "white", "blue");
        set_color ("status", "black", "gray");
        USE_ANSI_COLORS = 1;
      }
See Also

putenv, strlen, is_defined

19.5 implements

Synopsis

Name a private namespace

Usage

implements (String_Type name);

Description

The implements function may be used to name the private namespace associated with the current compilation unit. Doing so will enable access to the members of the namespace from outside the unit. The name of the global namespace is Global.

Example

Suppose that some file t.sl contains:

     implements ("Ts_Private");
     static define message (x)
     {
        Global->vmessage ("Ts_Private message: %s", x);
     }
     message ("hello");
will produce "Ts_Private message: hello". This message function may be accessed from outside via:
    Ts_Private->message ("hi");
Notes

Since message is an intrinsic function, it is global and may not be redefined in the global namespace.

19.6 putenv

Synopsis

Add or change an environment variable

Usage

putenv (String_Type s)

Description

This functions adds string s to the environment. Typically, s should of the form "name=value". The function signals a S-lang error upon failure.

Notes

This function is not available on all systems.

See Also

getenv, sprintf

Next Previous Contents


  • Introducing Sam Spade
  • Configuring Sam Spade
  • Using Sam Spade
    • Configuration
    • Basics
    • Header analysis
    • More advanced tools
  • Manuals and Tutorials
    • Spam-tracking 101
    • Spam-tracking 102: the many uses of DejaNews
      • HOW TO USE DEJANEWS:
    • Spam-tracking 103: the WHOIS database
      • QUERYING A DOMAIN NAME
      • USING WHOIS TO LOOK UP AN IP RANGE:
        • IP Blocks
      • USING WHOIS WHEN THERE ARE MULTIPLE RESULTS:
      • FOREIGN WHOIS SEARCHES:
    • Spam-tracking 104: a spammer unmasked
    • Internet Protocol Addressing
      • IP Address allocation
        • CIDR Allocation
        • But who's in charge of this address block?
        • Special Cases
          • Loopback
          • Private networks
        • Email to an IP address
      • Name resolution
        • Domain Name Service
        • DNS lookups
        • Reverse lookup
        • Advanced DNS
        • References
    • Regular expressions
      • Posix Basic Regular Expressions
      • An Introduction to Regexps
      • Literal Regexps
      • Character Sets
      • Subexpressions
      • Repeated Subexpressions
      • Optional Subexpressions
      • Counted Subexpressions
      • Alternative Subexpressions
      • Backreferences, Extractions and Substitutions
      • A Summary of Regexp Syntax
      • Ambiguous Patterns
      • Complications in Subexpression Matching
    • The Wildmat Format
      • Examples
    • Tip of the Day
      • The Spam Sketch
        • The cast:
        • The sketch:
    • Spam
      • Prehistory
      • Usenet spam
        • What can be done?
      • Email spam
        • What can be done?
    • Online Resources
  • Menus
    • File Menu
      • Save As...
      • Clear log
    • Edit Menu
      • Copy
      • Paste
      • Smart Paste
    • View Menu
    • Window Menu
    • Tools Menu
      • SMTP Relay Checking
      • DNS Zone Transfer
      • Active port scanning
      • Browse web
      • Crawl web
      • Checking for usenet cancels
    • Help Menu
  • Toolbar
    • Ping
    • DNS query
    • whois
      • Simple whois
      • Advanced whois
    • IP Block lookup
    • dig
    • Traceroute
    • Finger
    • Keepalive
  • Options Bar
  • Status bar
  • Technical support
    • Bug reports, enhancement requests
    • How to use the tools
    • Reading headers, tracing spam etc
  • Credits
    • Design, implementation, documentation
    • Inspiration
    • Alpha and Beta testers
    • And everyone else


How you can help

If you'd be interested in translating the online help and tutorials into languages other than english, contact me at spade@blighty.com